标签:
《挑战程序设计竞赛》2.3.1(POJ3624/NOIP2004采药问题)
最基础的01背包问题,标程性质,又二维和一维两种写法。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 const int MAXN=3403; 7 int w[MAXN]; 8 int v[MAXN]; 9 int W; 10 int f[MAXN][MAXN]; 11 12 int main() 13 { 14 int n; 15 scanf("%d%d",&n,&W); 16 memset(f,0,sizeof(f)); 17 for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]); 18 for (int i=1;i<=n;i++) 19 for (int j=1;j<=W;j++) 20 { 21 f[i][j]=f[i-1][j]; 22 if (j>=w[i]) f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]); 23 } 24 cout<<f[n][W]<<endl; 25 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 const int MAXN=12890; 7 int f[MAXN]; 8 9 int main() 10 { 11 int N,M; 12 scanf("%d%d",&N,&M); 13 memset(f,0,sizeof(f)); 14 for (int i=0;i<N;i++) 15 { 16 int w,d; 17 scanf("%d%d",&w,&d); 18 for (int j=M;j>=w;j--) 19 if ((f[j-w]+d) > f[j]) f[j]=f[j-w]+d; 20 } 21 cout<<f[M]<<endl; 22 return 0; 23 }
解释一下笔者第一次学01背包时容易遇到的困扰:
?为什么一维中要从后往前?因为一维并没有限制取到哪一个背包,从后往前防止再累加过一遍当前物品的基础上再次累加。
?为什么二维中需要if (j<w[i]) f[i][j]=f[i-1][j]?这个语句而一维中不需要类似语句?因为没有限制当前取到哪一个背包,取前一个背包代价为j时的情况已经记录了下来。
?二维背包能从后往前做吗?可以,对f[i][j]产生影响的只有它自身和f[i-1]中数据,前后次序无关。
?为什么一维中输出的时候输出f[M]或F[n][M]即可?背包问题中F[n][M]并不代表恰好取前n个包,总价值恰好为M,而是在这个范围内的最大值。不理解的话想一想以下的情况:若第一个物品价值为1,f[1][2]=f[0][1]+1,但是此时取到的总价值只有1,第二个下标却为2。
?二维背包能由当前位置推向后面吗?可以,见下面给出的程序
1 int dp() 2 { 3 for (int i=0;i<n;i++) 4 { 5 for (int i=0;j<=W;j++) 6 { 7 f[i+1][j]=max(f[i+1,j],f[i][j]); 8 if (j+w[i]<=W) f[i+1][j+w[i]]=max(f[i+1][j+w[i]],f[i][j]+v[i]); 9 } 10 } 11 cout<<f[n][W]<<endl; \\因为往后递推,最终数据保存在了f[n]中 12 }
明白了上述三个问题,01背包就基本可以算是理解透彻了。《挑战程序设计竞赛》在2.3.1中有对记忆化搜索的阐述,也可以关注一下。
至此,最基本的01背包问题就讲解结束了。
《挑战程序设计竞赛》2.3.1最长公共子序列(POJ1458)
01背包问题问题的拓展应用。思路非常简单,如下:
f[i][j]表示s1取到第i位,s2取到第j位时的最长公共子序列长度。如果s1[i]≠s2[j]在,则f[i,j]=max(f[i-1,j],f[i,j-1]),否则再增加一个比较对象f[i-1][j-1]+1
虽然思路是秒杀的,但是POJ1458涉及到字符串的读取,麻烦死了,几乎每次碰到字符串我都要跪,参考了他人的程序,折腾了好长时间,不过总算一遍就AC了。程序里的细节解释后期来说明,到时候会做一个字符串读取和处理的专题。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 const int MAXN=1001; 7 char s1[MAXN]; 8 char s2[MAXN]; 9 int f[MAXN][MAXN]; 10 11 int main() 12 { 13 14 while (scanf("%s%s",s1+1,s2+1)!=EOF) 15 { 16 int len1=strlen(s1+1),len2=strlen(s2+1); 17 memset(f,0,sizeof(f)); 18 for (int i=1;i<=len1;i++) 19 for (int j=1;j<=len2;j++) 20 { 21 f[i][j]=max(f[i-1][j],f[i][j-1]); 22 if (s1[i]==s2[j]) f[i][j]=f[i-1][j-1]+1; 23 } 24 cout<<f[len1][len2]<<endl; 25 } 26 return 0; 27 }
《挑战程序设计竞赛》2.3.2完全背包问题
完全背包问题和01背包问题的区别在于:每种物品可以挑选任意多件。完全背包问题和01背包问题一样,都有二维和一维的两种写法
TBC.
标签:
原文地址:http://www.cnblogs.com/iiyiyi/p/4602851.html