0-1背包 系列问题
hdu 1203
I NEED A OFFER!Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Description
Input
后面的m行,每行都有两个数据ai(整型),bi(实型)分别表示第i个学校的申请费用和可能拿到offer的概率。
输入的最后有两个0。
Output
Sample Input
Sample Output
Hint
You should use printf("%%") to print a '%'.
该问题化简后及是0-1背包。
化简:此题从反面求如何申请使成功概率最大。即考虑使失败的概率最小。也就是将申请的总费用限定在n时,怎样申请,使失败的概率最小。
n为背包的容量,每个学校会失败的概率为价值,要的申请费用为物品体积,怎样放使得价值最小。
做完这道题才意识到背包有两种方法:
第一种:开两维数组,a[i][j]表示第i个物品背包为j重时,背包的最大值。则a[i][j]=max{a[i-1][j-w[i]]+p[i] , a[i][j], a[i-1][j]};
第二种:开一维数组,a[i]表示背包为i重时的最大价值。则a[i]=max{a[i-w[j]]+p[j] , a[i]}.
这里需要注意的是,第二种因为没有变量标记装到了第几个物品,所以对重量做更新时,需要从最大值开始做起。
注意: 这里由于第一种要开两维数组,会造成超内存,所以这道题只能用第二种;这也正是第二种的优化的地方。
代码实现:
第一种
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 using namespace std; 5 double a[1005][10005]; 6 int w[1005]; 7 double p[1005]; 8 int main(){ 9 int n,m; 10 while(cin>>n>>m){ 11 if(n==0&&m==0) 12 break; 13 for(int i=1;i<=m;i++){ 14 scanf("%d%lf",&w[i],&p[i]); 15 p[i]=1-p[i]; 16 } 17 for(int i=0;i<=m;i++){ 18 for(int j=0;j<=n;j++){ 19 a[i][j]=1; 20 } 21 } 22 for(int k=1;k<=m;k++){ 23 for(int g=0;g<=n;g++){ 24 if(w[k]<=g){ 25 if(((a[k-1][g-w[k]]*p[k])<=(a[k][g]))){ 26 a[k][g]=a[k-1][g-w[k]]*p[k]; 27 } 28 if(a[k][g]>=a[k-1][g]) 29 a[k][g]=a[k-1][g]; 30 } 31 else 32 a[k][g]=a[k-1][g]; 33 } 34 } 35 printf("%.1lf%%\n",(1-a[m][n])*100); 36 } 37 return 0; 38 }
第二种
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; double a[10005]; int w[1005]; double p[1005]; int main(){ int n,m; while(cin>>n>>m){ if(n==0&&m==0) break; for(int i=1;i<=m;i++){ scanf("%d%lf",&w[i],&p[i]); p[i]=1-p[i]; } for(int i=0;i<=n;i++){ a[i]=1; } for(int k=1;k<=m;k++){ for(int g=n;g>=0;g--){ if(w[k]<=g){ if(((a[g-w[k]]*p[k])<=(a[g]))){ a[g]=a[g-w[k]]*p[k]; } } } } printf("%.1lf%%\n",(1-a[n])*100); } return 0; }
以上是每种物品都只能放一次的0-1背包,下题又有稍微的不同,在于其中的物品没有限制个数,也就是说,每种物品可以放任意多次。
hdu 1114
Piggy-BankTime Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Description
But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!
Input
Output
Sample Input
Sample Output
题意:给出存钱罐的原先的质量与装满之后的质量;给出存钱罐中可能存在的钱币的种数,接下来给出各种钱币的价值与重量,求这个装满的存钱罐中钱数的最小值。
这道题之前说的不同之处在于:物品的个数不限制,可以随意拿,求到限制的重量时的最小价值。
思路:
与上题很相似,但因为有条件不一样,不需要考虑钱币的重复。所以要考虑的子问题只有在背包的质量为 i 时,其中的最大质量为多少。还需要考虑的是存钱罐不能被刚好装
满的话,那么情况就不成立。
代码实现:
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int a[10005]; int w[505]; int p[505]; int main(){ int n1,n2; int m; cin>>m; while(m--){ scanf("%d%d",&n1,&n2); for(int g=0;g<=(n2-n1);g++){ a[g]=1000000000; } int n; scanf("%d",&n); int minn=1000000000; for(int i=1;i<=n;i++){ scanf("%d%d",&p[i],&w[i]); if(minn>w[i]) minn=w[i]; } a[0]=0; for(int j=minn;j<=(n2-n1);j++){ //这儿和普通的 0-1 背包不同 for(int k=1;k<=n;k++){ if(w[k]<=j){ if(a[j-w[k]]+p[k]<a[j]){ a[j]=a[j-w[k]]+p[k]; } } } } if(a[n2-n1]==1000000000) cout<<"This is impossible."<<endl; else cout<<"The minimum amount of money in the piggy-bank is "<<a[n2-n1]<<"."<<endl; } return 0; }