09-03 HDU_Steps3.3 各种背包 HDU1114 HDU1171 HDU2191 HDU2955 HDU3496 HDU2546 HDU1203 HDU3466

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



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值