DP(1) 背包
01背包模型
1 for (int i=1;i<=n;i++) 2 for (int j=1;j<=Max_c;j++) 3 f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+value[i]);
1.[noip2005pj]采药
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 int time[105],value[105],f[105][1005]; 7 8 int main() 9 { 10 int maxtime,tot; 11 scanf("%d%d",&maxtime,&tot); 12 for (int i=1;i<=tot;i++) 13 scanf("%d%d",&time[i],&value[i]); 14 15 memset(f,0,sizeof(f)); 16 for (int i=1;i<=tot;i++) 17 for (int j=1;j<=maxtime;j++) 18 { 19 f[i][j]=f[i-1][j]; 20 if (j>=time[i]) 21 f[i][j]=max(f[i][j],f[i-1][j-time[i]]+value[i]); 22 } 23 24 printf("%d",f[tot][maxtime]); 25 return 0; 26 }
2.[vijos1037]搭建双塔
题意:在n个数中选取若干个求和,得到两个相等的数,求最大数
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 bool f[100][2005][2005]; 7 int h[105]; 8 int main() 9 { 10 int n; 11 scanf("%d",&n); 12 memset(h,0,sizeof(h)); 13 int tot=0; 14 for (int i=1;i<=n;i++){ 15 scanf("%d",&h[i]); 16 tot+=h[i]; 17 } 18 tot/=2; 19 tot++; 20 memset(f,0,sizeof(f)); 21 f[0][0][0]=1; 22 for (int i=1;i<=n;i++) 23 for (int j=0;j<=tot;j++) 24 for (int k=0;k<=tot;k++){ 25 f[i][j][k]=f[i-1][j][k]; 26 if (j>=h[i]) f[i][j][k]=f[i][j][k]||f[i-1][j-h[i]][k]; 27 if (k>=h[i]) f[i][j][k]=f[i][j][k]||f[i-1][j][k-h[i]]; 28 } 29 int num=tot; 30 while (num>0) 31 { 32 if (f[n][num][num]==1) break; 33 num--; 34 } 35 if (num!=0) printf("%d\n",num); 36 else printf("Impossible"); 37 return 0; 38 }
f[i][j]表示前i块水晶,两塔高度差为j时,较低塔的高度
目标状态为f[n][0]
则对于h[i]需考虑四种情况:(可画图帮助理解)
1)不取 f[i][j]=f[i-1][j]
2)放在较低塔上,仍是较低的 f[i][j]=max(f[i][j],f[i-1][j+h[i]]+h[i])
3)放在较低塔上,变成较高的 f[i][j]=max(f[i][j],f[i-1][h[i]-j]+h[i]-j)
4)放在较高塔上 f[i][j]=max(f[i][j],f[i-1][j-h[i]])
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int f[105][2005],h[105]; int main(){ int n,tot=0; scanf("%d",&n); for (int i=1;i<=n;i++){ scanf("%d",&h[i]); tot+=h[i]; } memset(f,-1,sizeof(f)); f[0][0]=0; for (int i=1;i<=n;i++) for (int j=0;j<=tot;j++){ if (f[i-1][j]!=-1) f[i][j]=f[i-1][j]; if (f[i-1][j+h[i]]!=-1) f[i][j]=max(f[i][j],f[i-1][j+h[i]]+h[i]); if (j>=h[i]&&f[i-1][j+h[i]!=-1]) f[i][j]=max(f[i][j],f[i-1][j-h[i]]); if (h[i]>=j&&f[i-1][h[i]-j]!=-1) f[i][j]=max(f[i][j],f[i-1][h[i]-j]+h[i]-j); } int num=tot; if (f[n][0]>0) printf("%d",f[n][0]); else printf("Impossible"); return 0; }
3.[noip2007tg] 金明的预算方案
有依赖的背包
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int price[65],imp[65]; int infor[60][4]; int f[60][3500]; //预处理:a.主件,附件 // b.数字除以10 int main(){ int n,m,a,b,c; memset(infor,0,sizeof(infor)); scanf("%d%d",&n,&m); n/=10; for (int i=1;i<=m;i++){ scanf("%d%d%d",&price[i],&imp[i],&c); price[i]/=10; imp[i]*=price[i]; if (c==0) infor[i][0]=1; else{//是附件 infor[i][0]=0; infor[c][3]++; infor[c][infor[c][3]]=i; } } int Last=0; memset(f,0,sizeof(f)); for (int i=1;i<=m;i++){ if (infor[i][0]==0) continue; for (int j=0;j<=n;j++){ f[i][j]=f[Last][j]; //不取主件 if (j>=price[i]) f[i][j]=max(f[i][j],f[Last][j-price[i]]+imp[i]); //只取主件 if (infor[i][3]>0&&j>=price[i]+price[infor[i][1]]) f[i][j]=max(f[i][j],f[Last][j-price[i]-price[infor[i][1]]]+imp[i]+imp[infor[i][1]]); //取主件和第一件附件 if (infor[i][3]==2&&j>=price[i]+price[infor[i][2]]) f[i][j]=max(f[i][j],f[Last][j-price[i]-price[infor[i][2]]]+imp[i]+imp[infor[i][2]]); //取主件和第二件附件 if (infor[i][3]==2&&j>=price[i]+price[infor[i][1]]+price[infor[i][2]]) f[i][j]=max(f[i][j],f[Last][j-price[i]-price[infor[i][1]]-price[infor[i][2]]]+imp[i]+imp[infor[i][1]]+imp[infor[i][2]]); //取主件和两件附件 } Last=i; } printf("%d",f[Last][n]*10); return 0; }
总结:注意考虑到多种情况,还有各种小技巧的使用