Tag Archives: DP

POJ 1080-Human Gene Functions

POJ 1080-Human Gene Functions Human Gene Functions Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 17334 Accepted: 9637 Description It is well known that a human gene can be considered as a sequence, consisting of four nucleotides, which are simply denoted by four letters, A, C, G, and T. Biologists have been interested in identifying human genes and determining their functions, because these can be used to diagnose human diseases and to design new drugs for them. A human gene can be identified through a series of time-consuming biological experiments, often with the help of computer programs. Once a sequence of a gene is obtained, the next job is to determine its function. One of the methods for biologists to use in determining the function of a new gene sequence that they have just identified is to search a database with the new gene as a query. The database to be searched stores many gene sequences and their functions – many researchers have been submitting their genes and functions to the database and the database is freely accessible through the Internet. A database search will return a list of gene sequences from the database that are similar to the query gene. Biologists assume that sequence similarity often implies functional similarity. So, the function of the new gene might be one of the functions that the genes from the list have. To exactly determine which one is the right one another series of biological experiments will be needed. Your job is to make a program that compares two genes and determines their similarity as explained below. Your program may be used as a part of the database search if you can provide an efficient one. Given two genes AGTGATG and GTTAG, how similar are they? One of the methods to measure the similarity of two genes is called alignment. In an alignment, spaces are inserted, if necessary, in appropriate positions of the genes to make them equally long and score the resulting genes according to a scoring matrix. For example, one space is inserted into AGTGATG to result in AGTGAT-G, and three spaces are inserted into GTTAG to result in –GT–TAG. A space is denoted by a minus sign (-). The two genes are now of equal length. These two strings are aligned: AGTGAT-G -GT–TAG In this alignment, there are four matches, namely, G in the second position, T in the third, T in the sixth, and G in the eighth. Each pair of aligned characters is assigned a score according to the following scoring matrix. ![](http://poj.org/images/1080/1080_1.gif) denotes that a space-space match is not allowed. The score of the alignment above is (-3)+5+5+(-2)+(-3)+5+(-3)+5=9. Of course, many other alignments are possible. One is shown below (a different number of spaces are inserted into different positions): AGTGATG -GTTA-G This alignment gives a score of (-3)+5+5+(-2)+5+(-1) +5=14. So, this one is better than the previous one. As a matter of fact, this one is optimal since no other alignment can have a higher score. So, it is said that the similarity of the two genes is 14. Input The input consists of T test cases. The number of test cases ) (T is given in the first line of the input file. Each test case consists of two lines: each line contains an integer, the length of a gene, followed by a gene sequence. The length of each gene sequence is at least one and does not exceed 100. Output The output should print the similarity of each test case, one per line. Sample Input 2 7 AGTGATG 5 GTTAG 7 AGCTATT 9 AGCTTTAAA Sample Output 14 21 Source Taejon 2001


这一题是LCS(最长公共子序列)的变种,但是还是用DP来解决。 题目问给两个基因序列s1,s2插入’-‘,使得两个序列的`相似度`最大,求最大的相似度。假设f[i-1,j-1]为s1[1,…,i-1]和s2[1,…,j-1]的最大相似度,怎么通过f[i-1,j-1]来求f[i,j]呢?分如下三种情况:(这里假设f下标从0开始,s1,s2下标从1开始) 1. s1取第i个基因,s2取’-‘,则f[i,j]=f[i-1,j]+score[s1[i]],’-‘]; 2. s1取’-‘,s2取第j个基因,则f[i,j]=f[i,j-1]+score[‘-‘,s2[j]]; 3. s1取第i个基因,s2取第j个基因,则f[i,j]=f[i-1,j-1]+score[s1[i],s2[j]]; 所以f[i,j]即为上述三者的最大值。 对于初始值,自然有f[0,0]=0; f[0,j]=f[0,j-1]+score[‘-‘,s2[j]]; f[i,0]=f[i-1,0]+score[s1[i],’-‘]。 有了上述状态转换方程及初始值,便可以写出DP代码: [cpp] #include<iostream> #include<string> using namespace std; int score[‘T’+1][‘T’+1]; int inf=-5;//负无穷 const int MAX_N=101;//基因最大长度 int f[MAX_N][MAX_N];//f[i][j]表示s1[0…i-1]和s2[0…j-1]的相识度 //初始化分数表 void init_score() { score[‘A’][‘A’]=score[‘C’][‘C’]=score[‘G’][‘G’]=score[‘T’][‘T’]=5;//可以连续赋值 score[‘-‘][‘-‘]=inf;//负无穷 score[‘A’][‘C’]=score[‘C’][‘A’]=-1; score[‘A’][‘G’]=score[‘G’][‘A’]=-2; score[‘A’][‘T’]=score[‘T’][‘A’]=-1; score[‘A’][‘-‘]=score[‘-‘][‘A’]=-3;//’-‘的ASCII小于’T’ score[‘C’][‘G’]=score[‘G’][‘C’]=-3; score[‘C’][‘T’]=score[‘T’][‘C’]=-2; score[‘C’][‘-‘]=score[‘-‘][‘C’]=-4; score[‘G’][‘T’]=score[‘T’][‘G’]=-2; score[‘G’][‘-‘]=score[‘-‘][‘G’]=-2; score[‘T’][‘-‘]=score[‘-‘][‘T’]=-1; } //动态规划求值 int DP(string s1,int n1,string s2,int n2) { f[0][0]=0; for(int i=1;i<=n1;i++) //f[0][i]=f[0][i-1]+score[‘-‘][s1[i-1]];//注意顺序不要弄错了,这里表示i,s1为纵轴;j,s2为横轴 f[i][0]=f[i-1][0]+score[s1[i-1]][‘-‘]; for(int j=1;j<=n2;j++) //f[j][0]=f[j-1][0]+score[s2[j-1]][‘-‘]; f[0][j]=f[0][j-1]+score[‘-‘][s2[j-1]]; for(int i=1;i<=n1;i++) { for(int j=1;j<=n2;j++) { int a=f[i-1][j]+score[s1[i-1]][‘-‘]; int b=f[i][j-1]+score[‘-‘][s2[j-1]]; int c=f[i-1][j-1]+score[s1[i-1]][s2[j-1]]; int rs=a>b?a:b; f[i][j]=rs>c?rs:c;//三者最大值 } } return f[n1][n2]; } int main() { //freopen("input.txt","r",stdin); int t; cin>>t; init_score(); while(t–) { int n1,n2; string s1,s2; cin>>n1>>s1>>n2>>s2; cout<<DP(s1,n1,s2,n2)<<endl; } return 0; } [/cpp] 本代码提交AC,用时0MS,内存272K。]]>

hihoCoder 1037-数字三角形

hihoCoder 1037-数字三角形 #1037 : 数字三角形 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 问题描述 小Hi和小Ho在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他们来到了大洋彼岸的美国。美国人民的生活非常有意思,经常会有形形色色、奇奇怪怪的活动举办,这不,小Hi和小Ho刚刚下飞机,就赶上了当地的迷宫节活动。迷宫节里展览出来的迷宫都特别的有意思,但是小Ho却相中了一个其实并不怎么像迷宫的迷宫——因为这个迷宫的奖励非常丰富~ 于是小Ho找到了小Hi,让小Hi帮助他获取尽可能多的奖品,小Hi把手一伸道:“迷宫的介绍拿来!” 小Ho选择的迷宫是一个被称为“数字三角形”的n(n不超过200)层迷宫,这个迷宫的第i层有i个房间,分别编号为1..i。除去最后一层的房间,每一个房间都会有一些通往下一层的房间的楼梯,用符号来表示的话,就是从第i层的编号为j的房间出发会有两条路,一条通向第i+1层的编号为j的房间,另一条会通向第i+1层的编号为j+1的房间,而最后一层的所有房间都只有一条离开迷宫的道路。这样的道路都是单向的,也就是说当沿着这些道路前往下一层的房间或者离开迷宫之后,小Ho没有办法再次回到这个房间。迷宫里同时只会有一个参与者,而在每个参与者进入这个迷宫的时候,每个房间里都会生成一定数量的奖券,这些奖券可以在通过迷宫之后兑换各种奖品。小Ho的起点在第1层的编号为1的房间,现在小Ho悄悄向其他参与者弄清楚了每个房间里的奖券数量,希望小Hi帮他计算出他最多能获得多少奖券。 提示一:盲目贪心不可取,搜索计算太耗时 提示二:记忆深搜逞神威,宽度优先解难题 提示三:总结归纳提公式,减少冗余是真理 输入 每个测试点(输入文件)有且仅有一组测试数据。 每组测试数据的第一行为一个正整数n,表示这个迷宫的层数。 接下来的n行描述这个迷宫中每个房间的奖券数,其中第i行的第j个数代表着迷宫第i层的编号为j的房间中的奖券数量。 测试数据保证,有100%的数据满足n不超过100 对于100%的数据,迷宫的层数n不超过100 对于100%的数据,每个房间中的奖券数不超过1000 对于50%的数据,迷宫的层数不超过15(小Ho表示2^15才3万多呢,也就是说……) 对于10%的数据,迷宫的层数不超过1(小Hi很好奇你的边界情况处理的如何?~) 对于10%的数据,迷宫的构造满足:对于90%以上的结点,左边道路通向的房间中的奖券数比右边道路通向的房间中的奖券数要多。 对于10%的数据,迷宫的构造满足:对于90%以上的结点,左边道路通向的房间中的奖券数比右边道路通向的房间中的奖券数要少。 输出 对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的最多奖券数。 样例输入 5 2 6 4 1 2 8 4 0 9 6 6 5 5 3 6 样例输出 28


简单的动态规划问题,根据题目的描述:
从第i层的编号为j的房间出发会有两条路,一条通向第i+1层的编号为j的房间,另一条会通向第i+1层的编号为j+1的房间
假设best[i][j]为到达第i层第j个房间时能获得的最大奖券数,则有状态转换方程: $$best[i][j]=max \{ best[i-1][j],best[i-1][j-1] \} +reward[i][j] $$ 然后求最后一层的最大值,就是最终能够获取到的最大奖券数。 完整代码如下: [cpp] #include<iostream> using namespace std; int n; int reward[101][101];//reward[i][j]表示第i层第j个房间的奖券数 int best[101];//best[j]表示到达第i层第j个房间时能获取到的最大奖券数 int main() { //freopen("input.txt","r",stdin); cin>>n; for(int i=0;i<n;i++) for(int j=0;j<=i;j++) cin>>reward[i][j]; best[0]=reward[0][0]; for(int i=1;i<n;i++) { for(int j=i;j>=0;j–)//从右往左填表,这样只需要一行空间 { if(j==0) best[j]+=reward[i][j]; else { best[j]=(best[j]>best[j-1]?best[j]:best[j-1])+reward[i][j]; } } } int ans=0; for(int i=0;i<n;i++) if(best[i]>ans)//求最大值 ans=best[i]; cout<<ans; return 0; } [/cpp] 本代码提交AC,用时1MS,内存0MB。]]>

POJ 1837-Balance

POJ 1837-Balance Balance Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10845 Accepted: 6742 Description Gigel has a strange “balance” and he wants to poise it. Actually, the device is different from any other ordinary balance. It orders two arms of negligible weight and each arm’s length is 15. Some hooks are attached to these arms and Gigel wants to hang up some weights from his collection of G weights (1 <= G <= 20) knowing that these weights have distinct values in the range 1..25. Gigel may droop any weight of any hook but he is forced to use all the weights. Finally, Gigel managed to balance the device using the experience he gained at the National Olympiad in Informatics. Now he would like to know in how many ways the device can be balanced. Knowing the repartition of the hooks and the set of the weights write a program that calculates the number of possibilities to balance the device. It is guaranteed that will exist at least one solution for each test case at the evaluation. Input The input has the following structure: • the first line contains the number C (2 <= C <= 20) and the number G (2 <= G <= 20); • the next line contains C integer numbers (these numbers are also distinct and sorted in ascending order) in the range -15..15 representing the repartition of the hooks; each number represents the position relative to the center of the balance on the X axis (when no weights are attached the device is balanced and lined up to the X axis; the absolute value of the distances represents the distance between the hook and the balance center and the sign of the numbers determines the arm of the balance to which the hook is attached: ‘-‘ for the left arm and ‘+’ for the right arm); • on the next line there are G natural, distinct and sorted in ascending order numbers in the range 1..25 representing the weights’ values. Output The output contains the number M representing the number of possibilities to poise the balance. Sample Input 2 4 -2 3 3 4 5 8 Sample Output 2 Source Romania OI 2002


这一题题意为:给定一个天平,在天平两个臂上有若干个挂钩,给定若干砝码,问将所有砝码都挂上挂钩并使天平平衡的方法数有多少种?这一题有一定的难度,如果只有两个挂钩,问题比较好解决,可以看我的另一篇博客从一个数字序列中取若干个数字使得和为某个数,问共有多少种取数方案;但是这一题的挂钩有若干个,情况比较多,想过用动态规划、背包问题,但是一直想不出状态转换方程。 后来看了某前辈的解题报告,他提出了一个平衡度的概念,并推导出如下的状态转换方程:①dp[i][ j+ w[i]*c[k] ]= ∑(dp[i-1][j])。dp[i][j]表示前i个砝码使平衡度为j共有多少种方案数。 按照常理,我们得到的状态转换方程为:②dp[i][j] =∑(dp[i – 1][j – c[i] * w[i]]),表示dp[i][j]等于前i-1个砝码使平衡度为j-x的方案数之和,这个x就是第i个砝码挂在c个挂钩中的某一个产生的力臂。稍加推导就能得出①等价于②。 有了状态转换方程,我们可以很快的写出代码: [cpp] #include<iostream> using namespace std; int c[21]; int w[21]; int dp[21][15001]; int main() { int n,g; cin>>n>>g; for(int i=1;i<=n;i++) cin>>c[i]; for(int i=1;i<=g;i++) cin>>w[i]; dp[0][7500]=1;//不挂任何砝码的时候它本身就平衡了,所以是一种“挂法” for(int i=1;i<=g;i++)//对于每一个砝码 { for(int j=0;j<=15000;j++)//挂上去之后可能使天平出现0-15000中的任意一种状态 { if(dp[i-1][j])//如果这个状态之前出现过,则直接用 { for(int k=1;k<=n;k++) { dp[i][j+w[i]*c[k]]+=dp[i-1][j]; } } } } cout<<dp[g][7500]<<endl; return 0; } [/cpp] 代码提交AC,用时0MS,内存1060K。]]>

POJ 2151-Check the difficulty of problems

POJ 2151-Check the difficulty of problems Check the difficulty of problems Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 5066 Accepted: 2238 Description Organizing a programming contest is not an easy job. To avoid making the problems too difficult, the organizer usually expect the contest result satisfy the following two terms: 1. All of the teams solve at least one problem. 2. The champion (One of those teams that solve the most problems) solves at least a certain number of problems. Now the organizer has studied out the contest problems, and through the result of preliminary contest, the organizer can estimate the probability that a certain team can successfully solve a certain problem. Given the number of contest problems M, the number of teams T, and the number of problems N that the organizer expect the champion solve at least. We also assume that team i solves problem j with the probability Pij (1 <= i <= T, 1<= j <= M). Well, can you calculate the probability that all of the teams solve at least one problem, and at the same time the champion team solves at least N problems? Input The input consists of several test cases. The first line of each test case contains three integers M (0 < M <= 30), T (1 < T <= 1000) and N (0 < N <= M). Each of the following T lines contains M floating-point numbers in the range of [0,1]. In these T lines, the j-th number in the i-th line is just Pij. A test case of M = T = N = 0 indicates the end of input, and should not be processed. Output For each test case, please output the answer in a separate line. The result should be rounded to three digits after the decimal point. Sample Input 2 2 2 0.9 0.9 1 0.9 0 0 0 Sample Output 0.972 Source POJ Monthly,鲁小石


这题纯粹的数学概率题,不太好做。题目大意是:有m个问题,t个队,要求冠军至少解决n个问题,所有队至少解决1个问题,问这样的概率是多少。对于题目中给的样例,我们可以手算得出,分3种情况:a.第一个队是冠军,第二个队只解出一题0.9*0.9*1*0.1=0.081;b.第二个队是冠军,第一个队只解出一题(0.9*0.1+0.1*0.9)*1*0.9=0.162;c.两个队都是冠军,也就是并列冠军0.9*0.9*1*0.9=0.729;把这三种情况加起来就是0.972。 但是如果m,t的数字很大,问题就没那么简单了,解题思路比较难想到,可以参考以下两篇博客: 比较难理解的是概率的转化:
要求:每队至少解出一题且冠军队至少解出N道题的概率。
由于冠军队可以不止一队,即允许存在并列冠军。 则原来的所求的概率可以转化为:每队均至少做一题的概率P1 减去 每队做题数均在1到N-1之间的概率P2。 如果理解了上面那句话,使用概率DP就很容易写出代码: [cpp] #include<iostream> #include<cstdio> using namespace std; int m,t,n; double p[1001][31];//p[i][j]第i个队解出第j题的概率 double dp[1001][31][31];//dp[i][j][k]第i个队在前j个题中解出k个题的概率dp[i][j][k]=dp[i][j-1][k-1]*p[j][k]+dp[i][j-1][k]*(1-p[j][k])分第j个题能和不能解出来 double s[1001][31];//s[i][k]第i个队最多解出k个题的概率s[i][k]=dp[i][M][0]+dp[i][M][1]+…+dp[i][M][k] void solve() { for(int i=1;i<=t;i++) { dp[i][0][0]=1.0;//初始化,第i支队伍前0道题做对0道的概率为1 for(int j=1;j<=m;j++) { for(int k=0;k<=j;k++) { dp[i][j][k]=dp[i][j-1][k]*(1-p[i][j]); if(k!=0) dp[i][j][k]+=dp[i][j-1][k-1]*p[i][j]; } } s[i][0]=dp[i][m][0]; for(int j=1;j<=m;j++) s[i][j]=s[i][j-1]+dp[i][m][j]; } double p1=1.0,p2=1.0; for(int i=1;i<=t;i++) { p1*=(1-s[i][0]); p2*=(s[i][n-1]-s[i][0]); } printf("%.3f\n",p1-p2); } int main() { while(cin>>m>>t>>n&&m&&t&&n) { for(int i=1;i<=t;i++) for(int j=1;j<=m;j++) cin>>p[i][j]; solve(); } return 0; } [/cpp] 本代码提交AC,用时94MS,内存8228K。]]>

hihoCoder 1043-完全背包

hihoCoder 1043-完全背包 #1043 : 完全背包 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 且说之前的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了! 等等,这段故事为何似曾相识?这就要从平行宇宙理论说起了………总而言之,在另一个宇宙中,小Ho面临的问题发生了细微的变化! 小Ho现在手上有M张奖券,而奖品区有N种奖品,分别标号为1到N,其中第i种奖品需要need(i)张奖券进行兑换,并且可以兑换无数次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。 提示一: 切,不就是0~1变成了0~K么 提示二:强迫症患者总是会将状态转移方程优化一遍又一遍 提示三:同样不要忘了优化空间哦! 输入 每个测试点(输入文件)有且仅有一组测试数据。 每组测试数据的第一行为两个正整数N和M,表示奖品的种数,以及小Ho手中的奖券数。 接下来的n行描述每一行描述一种奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。 测试数据保证 对于100%的数据,N的值不超过500,M的值不超过10^5 对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3 输出 对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。 样例输入 5 1000 144 990 487 436 210 673 567 58 1056 897 样例输出 5940


这一题和之前的0-1背包很类似,只需要稍微修改一下就可以了。 我们先来复习一下之前的0-1背包。0-1背包对于一件物品,要么兑换,要么不兑换,只有两种可能。假设V[i,j]表示用j张奖券兑换前i个物品,则对于第i个物品,我们只有两种选择:1.兑换;2.不兑换。如果不兑换,则V[i,j]=V[i-1,j],也就是不要第i件物品,用j张奖券兑换前i-1个物品;如果兑换,则V[i,j]=V[i-1,j-need[i]]+value[i],也就是一定要第i件物品,所以+value[i],然后再看兑换完第i件物品后剩余的奖券兑换前i-1个物品能得到多少价值。最后就是求这两种情况的最大值。用数学公式表示就是: $$!V[i,j] = \begin{cases}0 & \text{if i=0 or j=0} \\V[i-1,j] & \text{if j < need[i]} \\max \{ V[i-1,j],V[i-1,j-need[i]]+value[i] \} & \text{if i>0 and j$\geq$ need[i]}\\\end{cases}$$ 上面是0-1背包的动态规划状态转换方程,详细的代码可以参考上面的链接。对于完全背包,只需稍微修改即可。 我们首先要理解完全背包的含义,完全背包不限制物品的个数,也就是只要奖券够多,可以兑换多个同一种物品但是对于某一个物品,也只有两种情况,要么兑换,要么不兑换,不可以将某一件物品拆分成小部分,只兑换其中的0.x。这个地方容易和分数背包混淆。分数背包和0-1类似,每一种物品也只有一个,你可以完整的兑换这一个物品,也可以只兑换这个物品的一部分,但是一种物品只有一个。分数背包可以用贪心算法快速求解,可以参考《算法导论第三版》中文版P243关于0-1背包和分数背包的讨论。 理解了完全背包的含义,我们再来推导其状态转换方程。这个时候对于第i个物品,我们有多种选择:1.不兑换;2.兑换1个;3.多换多个。同样的,我们要从中选一个能使价值最大的方案。如果不兑换,和0-1背包一样,有V[i,j]=V[i-1,j];但是如果兑换,是不是得一一尝试兑换1个、2个…的所有方案呢?可以这样做,但是还有一个更巧妙的方法,我们先给出公式再解释: $$!V[i,j] = \begin{cases}0 & \text{if i=0 or j=0} \\V[i-1,j] & \text{if j0 and j$\geq$ need[i]}\\\end{cases}$$ 大家仔细对比0-1背包和完全背包的状态转换方程,只改动了一个微小的地方。对于第i个物品,我们如果兑换,则肯定要+value[i],这是剩下j-need[i]张奖券,因为第i个物品可以兑换多个,所以我们还是用j-need[i]张奖券兑换前i个物品,而不是只兑换前i-1个物品,即V[i,j]=V[i,j-need[i]]+value[i]这样的话,程序自然还会考虑选择第i个物品,也就是兑换多个第i个物品。这就是0-1背包和完全背包的重要区别。 理解了这一点,我们很快就可以写出程序了。代码如下: [cpp] #include <iostream> #include<vector> using namespace std; int main() { int n,m; cin>>n>>m; vector<int> need_vi(n),value_vi(n); for(int i=0;i<n;i++) cin>>need_vi[i]>>value_vi[i]; vector<int> V(m+1,0);//V[j]表示有j张奖券,装前i个奖品获得的最大评分 for(int i=0;i<n;i++)//V[j]表示有j张奖券,装前i个奖品获得的最大评分,每个奖品可以装多个 { for(int j=need_vi[i];j<=m;j++)//从前往后填表,因为这是完全背包 { V[j]=(V[j-need_vi[i]]+value_vi[i]>V[j])?(V[j-need_vi[i]]+value_vi[i]):V[j]; } } cout<<V[m]; return 0; } [/cpp] 具体到写代码时,因为我们正是要利用兑换第i个物品时j=j-need[i]时的值,所以我们从左往右填(从前往后填),这样后面就能利用前面已经修改过的值了。大家可以自己画个表格填一下就知道了,同时请参考联系我之前关于0-1背包的博客。 本代码提交AC,用时148MS,内存0MB。]]>

hihoCoder 1038-01背包

hihoCoder 1038-01背包 #1038 : 01背包 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了! 小Ho现在手上有M张奖券,而奖品区有N件奖品,分别标号为1到N,其中第i件奖品需要need(i)张奖券进行兑换,同时也只能兑换一次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。 提示一:合理抽象问题、定义状态是动态规划最关键的一步 提示二:说过了减少时间消耗,我们再来看看如何减少空间消耗 输入 每个测试点(输入文件)有且仅有一组测试数据。 每组测试数据的第一行为两个正整数N和M,表示奖品的个数,以及小Ho手中的奖券数。 接下来的n行描述每一行描述一个奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。 测试数据保证 对于100%的数据,N的值不超过500,M的值不超过10^5 对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3 输出 对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。 样例输入 5 1000 144 990 487 436 210 673 567 58 1056 897 样例输出 2099


很简单的0-1背包问题。重新复习一下算法课本P140就好。注意填表的时候,从右往左填,这样只需要一行的空间。如果从左往右填只用一行空间的话,新的数据会覆盖老的数据,但是后面填表的过程中要用到老的数据,所以不行。代码如下: [cpp] #include<iostream> #include<vector> using namespace std; int main() { int n,m; cin>>n>>m; vector<int> need_vi(n);//所需奖券 vector<int> value_vi(n);//评分值 for(int i=0;i<n;i++) cin>>need_vi[i]>>value_vi[i]; vector<int> V(m+1,0);//V[j]表示有j张奖券,装前i个奖品获得的最大评分 for(int i=0;i<n;i++)//V[j]表示有j张奖券,装前i个奖品获得的最大评分 { for(int j=m;j>=need_vi[i];j–)//从后往前填表,能减少空间 { V[j]=(V[j-need_vi[i]]+value_vi[i]>V[j])?(V[j-need_vi[i]]+value_vi[i]):V[j];//要这件奖品和不要的对比 } } cout<<V[m]; return 0; } [/cpp] 代码提交AC,用时148MS,内存0MB。]]>