背包专题、

原创 2016年08月31日 06:48:42

背包资料:参考背包九讲

HDU 2546

思路:卡上的剩余金额大于或等于5元,就一定可以购买成功,注意这句话,那么我们考虑如果我们拿这最后五块钱去买最贵的东西的话肯定是可以的,但此时能选得物品不包括最贵的那一样,因为最贵的那一样我们会拿开始就拿出来的5块钱去买、那么问题就简化成了一个01背包

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; 
const int qq=1000+10;
int num[qq];
int dp[qq];
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF){
		if(!n)	break;
		for(int i=1; i<=n; ++i)
			scanf("%d",&num[i]);
		int m;scanf("%d",&m);
		if(m<5){	//少考虑这个情况 
			printf("%d\n",m);
			continue;
		}
		m-=5;
		memset(dp,0,sizeof(dp));
		sort(num+1,num+n+1);
		for(int i=1; i<=n-1; ++i)
			for(int j=m; j>=num[i]; --j)	//从后往前取保证每个数只取一次、 
				dp[j]=max(dp[j], dp[j-num[i]]+num[i]);
		printf("%d\n",m+5-dp[m]-num[n]);
	}
	return 0;
}

注意理解为何是逆序遍历、想想dp状态的由来

提醒自己要细心,每一种情况都考虑完

HDU 1203

思路:题目要求至少获得一份offer的概率,这个有点不好求,那么我们反过来想,考虑他的反面求一份offer都获得不了的概率,定义状态dp[j]为当钱为j时一份offer也拿不到的最小概率,那么 dp[j] = min(dp[j], dp[j-v]*b)  , 变种的01背包问题、

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int qq=1e4+10;
double dp[qq];
int val[qq];
double bility[qq];
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF){
		if(n==0&&m==0)	break;
		for(int i=0; i<m; ++i){
			scanf("%d%lf",&val[i],&bility[i]);
			bility[i]=1-bility[i];
		}
		for(int i=0; i<=n; ++i)	dp[i]=1.0;
		for(int i=0; i<m; ++i)
			for(int j=n; j>=val[i]; --j)
				dp[j]=min(dp[j], dp[j-val[i]]*bility[i]);
		printf("%.1lf%%\n",(1.0-dp[n])*100);
				
	}
	return 0;
}

HDU 2639

参考资料:传送门

题意:01背包求第k解、

思路:定义状态dp[j][k]代表当背包容量为j时第k解、

#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int qq = 105;
int n,v,k;
int price[qq],value[qq];
int a[35],b[35];
int dp[qq*10][35];
int main()
{
	int t;scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&v,&k);
		int i,j,l,x,y,z;
		for(i=1; i<=n; ++i)	scanf("%d",&value[i]);
		for(i=1; i<=n; ++i)	scanf("%d",&price[i]);
		memset(dp, 0, sizeof(dp));
		for(i=1; i<=n; ++i)
			for(j=v; j>=price[i]; --j){
				for(l=1; l<=k; ++l){
					a[l]=dp[j][l];
					b[l]=dp[j-price[i]][l]+value[i];
				}
				x=y=z=1;
				a[l]=b[l]=-1;
				while(z<=k && (a[x]!=-1 || b[y]!=-1)){
					if(a[x]>b[y])	dp[j][z]=a[x],x++;
					else		dp[j][z]=b[y],y++;
					if(dp[j][z]!=dp[j][z-1])	z++;
				}
			}
		printf("%d\n",dp[v][k]);	 
	}
	return 0;
} 


HDU 1114

题意:给出一个存钱罐的为空时的重量和装满时的重量,然后给出n种价值的硬币,价值为 v,重量为w,求装满存钱罐时的最少价值。

思路:完全背包问题,注意范围!!!

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int qq=5510;
const int MAX=1e8;
int price[qq],value[qq];
int dp[100100]; 
int main()
{
	int t;scanf("%d",&t);
	while(t--){
		int n,m;scanf("%d%d",&n,&m);
		int v=m-n;
		int k;scanf("%d",&k);
		for(int i=0; i<k; ++i)
			scanf("%d%d",&value[i],&price[i]);
		for(int i=1; i<=v; ++i)	dp[i]=MAX;
		dp[0]=0;
		for(int i=0; i<k; ++i)	//这里是k不是n、习惯性的当成n了 
			for(int j=price[i]; j<=v; ++j)	//以后还是不要出现n表示别的东西吧、 
				dp[j]=min(dp[j], dp[j-price[i]]+value[i]);
		if(dp[v]!=MAX)	printf("The minimum amount of money in the piggy-bank is %d.\n",dp[v]);
		else		printf("This is impossible.\n");
	}
	return 0;
}

HDU 2191

思路:根据背包九讲直接转化成01背包、

注意数据范围!!!

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int qq = 205;
int price[qq*10],value[qq*10];
int dp[qq];
int main()
{
	int t;scanf("%d",&t);
	while(t--){
		int n,m;scanf("%d%d",&n,&m);
		int k=0;
		int c,a,b;
		for(int i=0; i<m; ++i){
			scanf("%d%d%d",&a,&b,&c);
			int t=1;
			while(c>t){
				price[k]=t*a;
				value[k]=t*b;
				c=c-t;
				k++;
				t<<=1;
			}
			if(c>0){
				price[k]=c*a;
				value[k]=c*b;
				k++;
			}
		}
		memset(dp, 0, sizeof(dp));
		for(int i=0; i<k; ++i)
			for(int j=n; j>=price[i]; --j)
				dp[j]=max(dp[j], dp[j-price[i]]+value[i]);
		printf("%d\n",dp[n]);
	}
	return 0;
}

HDU 1059

题意:给出价值为1到6的硬币的个数,求是否能把平分这些硬币,使得两个人所获得的钱一样多

思路:还是拆成01背包然后对容量为sum/2的背包进行dp,如果最后dp[sum/2]的状态存在,那么就可以平分

#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<stack>
#include<vector>
#include<utility>
using namespace std;
const int qq = 1e5+10;
int price[qq],value[qq];
int dp[qq];
int num[10];
int main()
{
	int k=1;
	while(scanf("%d",&num[1])!=EOF){
		int count=0;
		int c=num[1];
		for(int i=2; i<=6; ++i){
			scanf("%d",&num[i]);
			c+=num[i];
		}
		if(c==0)	break;
		int sum=0; 
		for(int i=1; i<=6; ++i){
			int t=1;
			sum+=i*num[i];
			while(num[i]>=t){
				price[count]=t*i;
				value[count]=t*i;
				count++;
				num[i]=num[i]-t;
				t=t<<1;
			}
			if(num[i]){
				price[count]=i*num[i];
				value[count]=i*num[i];
				++count;
			}
		}
		printf("Collection #%d:\n",k++);
		if(sum%2==1){
			printf("Can't be divided.\n\n");
			continue;
		}
		memset(dp, 0, sizeof(dp));
		for(int i=0; i<count; ++i)	
			for(int j=sum/2; j>=price[i]; --j)
				 dp[j]=max(dp[j], dp[j-price[i]]+value[i]);
		if(dp[sum/2]==sum-sum/2)	printf("Can be divided.\n");
		else	printf("Can't be divided.\n");
		printf("\n");
	}
	return 0;
}


HDU 3466

思路:排序后才能看成背包问题、

参考:传送门

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int qq=550;
struct Item
{
	int p,q,w;
	int t;
	bool operator < (const Item a)
	{
		return t<a.t;
	}
}item[qq];
int n,m;
int dp[qq*10];	//注意dp的范围、 
int Quicksort(int l, int r)
{
	int i=l,j=r;
	int x=item[l].t;
	Item c=item[l];	//很久没写快排了、 今天一写 错误竟然那么多、 
	while(i<j){		//感觉我有点死记模版了、  但是思路我确实是知道的、 
		while(i<j && item[j].t>=x)	--j;		//下次以思路来代模版、 
		item[i]=item[j];
		while(i<j && item[i].t<=x)	++i;
		item[j]=item[i];	
	}
	item[i]=c;
	return i;
}
void Quick(int l, int r)
{
	if(l<r){
		int temp=Quicksort(l,r);
		Quick(l,temp);
		Quick(temp+1, r);
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=0; i<n; ++i){
			scanf("%d%d%d",&item[i].p,&item[i].q,&item[i].w);
			item[i].t=item[i].q-item[i].p;
		}
		Quick(0,n-1);
		//for(int i=0 ;i<n; ++i)
		//	printf("%d\n",item[i].t);
	//	sort(item,item+n);
		memset(dp, 0, sizeof(dp));
		for(int i=0; i<n; ++i)
			for(int j=m; j>=item[i].q; --j)	
				dp[j]=max(dp[j], dp[j-item[i].p]+item[i].w);
		printf("%d\n",dp[m]);
	}
	return 0;
}


POJ 2063

题意:给出本金n,是和要存的年数,然后给出k,代表有k中存钱方式,存期都是一年,你存钱a,一年后可以得到利息b,问m年后能得到的最大利息 + 本金

思路:其实也就是个完全背包,但是这里加了年数,那么对年数进行循环行了,但是这样问题还没解决,TEL,  因为本金是不断变大的, 循环的时间也越来越多,所以我们要想办法压缩时间,The value of a bond is always a multiple of $1 000. 注意这句话,这句话是在input里面, 那么显然我们可以对价值进行压缩。

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int qq=5e4+10;
const int inf=1e9;
int dp[qq],price[110],value[110];
int main() 
{
	int t;scanf("%d",&t);
	while(t--){
		int n,m;scanf("%d%d",&n,&m);
		int k;scanf("%d",&k);
		for(int i=1; i<=k; ++i){
			scanf("%d%d",&price[i],&value[i]);
			price[i]/=1000;		//对背包大小进行压缩、 
		}
		int val=n;
		for(int i=1; i<=m; ++i){
			int maxn=0; 
			int s=val/1000;	//继续压缩、
			memset(dp, 0, sizeof(dp)); 
			for(int j=1; j<=k; ++j)
				for(int l=price[j]; l<=s; ++l)
					dp[l]=max(dp[l], dp[l-price[j]]+value[j]);
			val+=dp[s];	//本金+利息 
		}
		printf("%d\n",val);
	}
	return 0;
}

POJ 3181

思路:整数划分的dp,这题的结果会溢出long long的范围,用下面的方法优化了

整数划分思想:传送门

参考:kuangbin

#include<cstdio>
#include<cstring>
const int qq=1000+10;
typedef long long ll;
ll a[qq];
ll b[qq];
int main()
{
	int n,k;scanf("%d%d",&n,&k);
	ll inf=1;
	for(int i=0; i<18; ++i)	inf*=10;
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	a[0]=1;		//优化到一维后注意初状态、 
	for(int i=1; i<=k; ++i)
		for(int j=1; j<=n; ++j){
			if(j<i){	//j<i说明j-i是个负数 不存在dp[j-i][i]的情况 ;
				continue;
			}
			b[j]=b[j]+b[j-i]+(a[j]+a[j-i])/inf;
			a[j]=(a[j]+a[j-i])%inf;
		}
	if(b[n]!=0)	printf("%I64d%018I64d",b[n],a[n]);
	else	printf("%I64d\n",a[n]);
	return 0;
}


POJ 2184

题意:给出n行数,每一行给出一个si和fi,要求选出任意行使得所有si+fi的总和最大,且si总和是fi的总和都要大于等于0

思路:01背包的变种,参考:kuangbin, 为什么要分正序和逆序呢? 想想开始我们做普通01背包的时候为什么逆序,因为状态更新的由来!!!

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int dp[200010];
int v[110],w[110];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=0; i<n; ++i)
		scanf("%d%d",&v[i],&w[i]);
	for(int i=0; i<=200000; ++i)	dp[i]=-INF;
	dp[100000]=0;
	for(int i=0; i<n; ++i)
		if(v[i]>0){
			for(int j=200000; j>=v[i]; --j)
				if(dp[j-v[i]]>-INF)
					dp[j]=max(dp[j], dp[j-v[i]]+w[i]);
		}	
		else{
			for(int j=0; j<=200000+v[i]; ++j)
				if(dp[j-v[i]]>-INF)
					dp[j]=max(dp[j], dp[j-v[i]]+w[i]);
		}
	int maxn=0;
	for(int i=100000; i<=200000; ++i)
		if(dp[i]>=0 && dp[i]+i-100000>maxn)
			maxn=dp[i]+i-100000;
	printf("%d\n",maxn);
	return 0;
}

HDU 2955

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int qq = 105;
int v[qq];
double probli[qq];
double dp[qq*100];
int main()
{
	int t;scanf("%d",&t);
	while(t--){
		double lower;scanf("%lf",&lower);
		int n;scanf("%d",&n);
		int sum=0;
		lower=1.0-lower;
		for(int i=0; i<n; ++i){
			scanf("%d%lf",&v[i],&probli[i]);
			sum+=v[i];
			probli[i]=1-probli[i];
		}
		// 通过这题我知道了初始状态的重要性、 
		//而且这题的钱数都是一些特定的数、 有些状态可能达不到、比如
		//第二组sample input  偷取1钱的安全概率始终是0、 因为这个状态不存在、
		//还要记得每个状态都是由前面的状态递推而来、
		//所以初始状态真的很重要、 
		for(int i=1; i<=sum; ++i)	dp[i]=0;
		dp[0]=1;		//当盗取0钱的时候 安全概率肯定是1、 
		for(int i=0; i<n; ++i){
			for(int j=sum; j>=v[i]; --j){
				dp[j]=max(dp[j], dp[j-v[i]]*probli[i]);
			//	printf("%lf ",dp[j]);
			}
			//printf("\n");		
		}
		int maxn=0;
		for(int i=1; i<=sum; ++i)
				if(dp[i]>=lower&&maxn<i)
						maxn=i;
		printf("%d\n",maxn);
	}
	return 0;
} 


HDU 4342

参考:kuangbin

这题真的太妙了、 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int qq = 200 + 10;
int group[qq][qq];	//代表第i组第j个物品的位置、(存的物品是位置) 
int dp[40010];
struct Node
{
	int x,y;
	int v,t;
	bool operator < (const Node a)	//考虑到斜率x可能为0、所以用乘法来比较 
	{
		if(y*a.x==x*a.y)	return y<a.y;
		return y*a.x<x*a.y; 
		
	}
}node[qq];
int main()
{
	int n,m;
	int t=1;
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=0; i<n; ++i)
			scanf("%d%d%d%d",&node[i].x,&node[i].y,&node[i].t,&node[i].v);
		sort(node, node+n);
		int kind=0;
		for(int i=0; i<n; ++i){
			group[kind][0]=0;
			group[kind][++group[kind][0]]=i;
			int x1=node[i].x;
			int y1=node[i].y;
			int a=node[i].t;
			int b=node[i].v;
			for(i++; i<n; ++i){	//进行分组、 
				int x2=node[i].x;
				int y2=node[i].y;
				a+=node[i].t;
				b+=node[i].v;
				if(x1*y2==x2*y1){
					group[kind][++group[kind][0]]=i;
					node[i].t=a;
					node[i].v=b;
				}
				else{
					--i;break;
				}
			}
			kind++;
		}
		memset(dp, 0, sizeof(dp));	//其实和01背包差不多、 
		for(int k=0; k<kind; ++k)	//预处理起来麻烦一点、 
			for(int j=m; j>=0; --j)
				for(int i=1; i<=group[k][0]; ++i)
					if(j-node[group[k][i]].t>=0)
						dp[j]=max(dp[j], dp[j-node[group[k][i]].t]+node[group[k][i]].v);
		printf("Case %d: %d\n",t++,dp[m]);
	}
	return 0;
}


版权声明:吸猫大法、

相关文章推荐

背包专题 算法

  • 2011-10-03 14:50
  • 396KB
  • 下载

【背包专题汇总】

菜鸟要开始写一个背包系列的博客了,会有持续更新哦,欢迎个位大牛指正~ 【01背包】 1.问题描述: 有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的价值是Wi。...

分组背包专题小结

先给出分组背包的介绍(参考自:http://blog.csdn.net/nywsp/article/details/7737158) 问题 有N件物品和一个容量为V的背包。第i件物品的费用...

【专题】背包问题

例四:背包问题1.0-1背包 有N件物品和一个容量为V的背包。第i件物品所占容量是v[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 设状态dp[i][j]表示前只放前i个物品容量...

POJ 多重背包专题

POJ 1014  Dividing 这道题用背包做有两种解法,一种是拆分法,另一种是很神的O(VN)的DP法。 拆分法: #include #include #include #in...

背包解决硬币问题专题

link:http://acm.hdu.edu.cn/showproblem.php?pid=1284 钱币兑换问题 Time Limit: 2000/1000 MS (Java/Othe...

DP专题->01背包

首先01背包题目的雏形是 有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 从这个题目中可以看出,01背包的特点就是:每种物品仅...

【背包专题】01背包

暑假集训开始了,按照队里的分配,我是弄DP的,嘛,于是我又一次的开始了从01背包开始学习,昨天将杭电的几道01背包重新做了一遍,下面讲讲我自己对于01背包的理解。   首先01背包题目的雏形是 有N件...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)