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的值排序后再背包即可