转自:http://blog.csdn.net/swm8023/article/details/6762190
背包总结,收藏之。
HDU_STEPS3.3 主要是背包问题
3.3.1 HDU1114 Piggy-Bank 完全背包,要正好装满
- #include <cstdio>
- #include <string.h>
- using namespace std;
- int e,f,n;
- int d[10005];
- int p[505],w[505];
- int main(){
- int cas;
- scanf("%d",&cas);
- while(cas--){
- scanf("%d%d",&e,&f);
- e=f-e;
- scanf("%d",&n);
- for(int i=1;i<=n;i++)scanf("%d%d",&p[i],&w[i]);
- //DP
- for(int i=1;i<=e;i++)d[i]=1e9;
- d[0]=0;
- for(int i=1;i<=n;i++){
- for(int j=w[i];j<=e;j++){
- if(d[j-w[i]]+p[i]<d[j])d[j]=d[j-w[i]]+p[i];
- }
- }
- if(d[e]==1e9)printf("This is impossible.\n");
- else printf("The minimum amount of money in the piggy-bank is %d.\n",d[e]);
- }
- return 0;
- }
3.3.2 HDU1171 Big Event in HDU
多重背包,每种物品个数有限制,用一个数组标记该值是否可达
最后从中间像两边扫描,先找到的值为A和B中的一个
- #include <cstdio>
- #include <string.h>
- using namespace std;
- int n,tot;
- int v[55],m[55];
- bool d[250005];
- int main(){
- while(scanf("%d",&n)&&n>=0){
- int tot=0;
- for(int i=1;i<=n;i++){
- scanf("%d%d",&v[i],&m[i]);
- tot+=v[i]*m[i];
- }
- //多重背包 每种物品数目固定 DP
- memset(d,0,sizeof d);
- d[0]=true;
- for(int i=1;i<=n;i++){
- for(int k=1;k<=m[i];k++){
- for(int j=tot;j>=v[i];j--){
- if(d[j-v[i]])d[j]=true;
- }
- }
- }
- //输出答案
- int ind=0;
- for(int i=tot/2,j=tot/2;;i++,j--){
- if(d[i]){ind=i;break;}
- if(d[j]){ind=j;break;}
- }
- if(ind<tot-ind)ind=tot-ind;
- printf("%d %d\n",ind,tot-ind);
- }
- return 0;
- }
3.3.3 HDU2191 悼念512汶川地震
多重背包,跟新某一点时选择价值更大的组合
- memset(d,-1,sizeof d);
- d[0]=0;
- for(int i=1;i<=m;i++){
- for(int j=1;j<=c[i];j++){
- for(int k=n;k>=p[i];k--){
- if(d[k-p[i]]!=-1&&d[k-p[i]]+h[i]>d[k]){
- d[k]=d[k-p[i]]+h[i];
- }
- }
- }
- }
3.3.4 HDU2955 Robberies
题目看了半天才看懂,就是求小偷最多能偷几个银行,DP使该点不被捉的概率最大
假如偷了N个银行,被捉概率是p1,p2..pn ,则不被捉的概率是(1-p1)(1-p2)...(1-pn)
- #include <cstdio>
- using namespace std;
- int cas,mi[101],n,tot;
- double pi[101],p;
- double d[10000];
- int main(){
- scanf("%d",&cas);
- while(cas--){
- for(int i=0;i<=10000;i++)d[i]=0;
- tot=0;
- d[0]=1;
- scanf("%lf%d",&p,&n);
- for(int i=1;i<=n;i++){
- scanf("%d%lf",&mi[i],&pi[i]);
- tot+=mi[i];
- }
- //背包 转换为乘法 使同一个点不被捉的概率最大
- for(int i=1;i<=n;i++){
- for(int j=tot;j>=mi[i];j--){
- if(d[j]<d[j-mi[i]]*(1-pi[i]))d[j]=d[j-mi[i]]*(1-pi[i]);
- }
- }
- for(int i=tot;i>=0;i--){
- if(d[i]>=1-p){printf("%d\n",i);break;}
- }
- }
- return 0;
- }
3.3.5 HDU3496 Watch the Movie
二维背包,背包中的物品个数有限制
状态表示为d[n][m],n为使用物品的个数,m为背包中物品的个数
注意循环的方向,要保证d[n][m]中有m件物品,最后d[N][M]即为所求
- #include <cstdio>
- #include <string>
- #include <math.h>
- #include <string.h>
- #include <stdlib.h>
- #include <iostream>
- #include <algorithm>
- using namespace std;
- int cas,n,m,l;
- int w[105],v[105],d[105][1005];
- int main(){
- scanf("%d",&cas);
- while(cas--){
- scanf("%d%d%d",&n,&m,&l);
- for(int i=1;i<=n;i++)scanf("%d%d",&w[i],&v[i]);
- //DP
- memset(d,-1,sizeof d);
- d[0][0]=0;
- for(int i=1;i<=n;i++){
- for(int j=m;j>=1;j--){//注意方向,很重要
- for(int k=l;k>=w[i];k--){
- if(d[j-1][k-w[i]]!=-1)d[j][k]=max(d[j][k],d[j-1][k-w[i]]+v[i]);
- }
- }
- }
- int r=0;
- for(int i=1;i<=l;i++)r=max(r,d[m][i]);
- printf("%d\n",r);
- }
- //system("pause");
- return 0;
- }
3.3.6 HDU2546 饭卡
一开始直接背包DP,当饭卡余额大于5元时才跟新,最后最大值和卡上的钱的差就是余额..但是提交WA了
比如卡上8元,两种商品7元和2元,如果按照7,2的顺序跟新,2元的商品就不会去买了,因为买了7元的商品后余额就只有1元了..加上一个排序就过了,价值大的商品的后买,这样可以保证卡上最后余额最少.
- #include <cstdio>
- #include <string.h>
- #include <algorithm>
- using namespace std;
- int main(){
- int n,p[1005],m,d[1100];
- while(scanf("%d",&n),n){
- for(int i=1;i<=n;i++)scanf("%d",&p[i]);
- sort(p+1,p+1+n);
- scanf("%d",&m);
- memset(d,0,sizeof d);
- d[0]=1;
- for(int i=1;i<=n;i++){
- //放宽50因为可能是负的
- for(int j=m+55;j>=p[i];j--){
- //余额大于5元才可
- if(j-p[i]<=m-5&&d[j-p[i]])d[j]=1;
- }
- }
- for(int i=m+55;i>=0;i--){
- if(d[i]==1){
- printf("%d\n",m-i);
- break;
- }
- }
- }
- return 0;
- }
3.3.7 HDU1203 I NEED A OFFER
这题与3.3.4类似,背包的对象也是概率,是不能拿到Offer的概率最小p=(1-a1)(1-a2)...(1-an)
3.3.8 HDU3466 Proud Merchants
带有贪心性质的背包,按Q-P的值排序后再背包即可