2021.6.8背包总结

T1


解析:二维01背包板子,不多赘述

#include<bits/stdc++.h>
using namespace std;
const int N = 405;
int w1[N],w2[N],val[N],dp[N][N];
int m1,m2,n;
int main()
{
	scanf("%d%d",&m1,&m2);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&val[i],&w1[i],&w2[i]);
	for(int i=1;i<=n;i++)
		for(int j=m1;j>=w1[i];j--)
			for(int k=m2;k>=w2[i];k--)
				dp[j][k]=max(dp[j][k],dp[j-w1[i]][k-w2[i]]+val[i]);
	printf("%d\n",dp[m1][m2]);
	return 0;
}
/*
6 5
4
10 2 2 
20 3 2
40 4 3
30 3 3
*/

T2

解析:输出答案就是简单的有限背包,关键在于如何输出路径。过程中num[i][j]表示第i个物品在总体积达到j时选了多少个

最后递归输出路径,但是要先找到达到最大价值所需的最少费用(now),否则输出就会错乱

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 505;
int n,m,p[N],w[105];
ll eff[105][105],sum;
ll ans[N][N];
ll dp[N][N];
ll num[N][N];
ll pre[N];
void print(int i,int now)
{
	if(!i) return;
	print(i-1,now-num[i][now]*w[i]);
	printf("%d\n",num[i][now]);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&w[i],&p[i]);
		for(int j=1;j<=p[i];j++)
		{
			scanf("%lld",&eff[i][j]);
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=m;j>=w[i];j--)
			for(int k=1;k<=p[i];k++)
				if(j>=k*w[i]) 
				{
					if(dp[j][0]<dp[j-k*w[i]][0]+eff[i][k])
					{
						dp[j][0]=dp[j-k*w[i]][0]+eff[i][k];
						num[i][j]=k;
					}
				}
	int now=m;
	printf("%lld\n",dp[m][0]);
	while(dp[now][0]==dp[now-1][0]) now--;
	print(n,now);
	return 0;
}
/*
3 10
1 3 1 2 2 
2 3 2 4 6 
3 3 2 1 10
*/

(或者也可以用dp记录每个物品怎么选)

代码如下

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 505;
int n,m,p[N],w[105];
ll eff[105][105],sum;
ll ans[N][N];
ll dp[N][N];
ll pre[N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&w[i],&p[i]);
		for(int j=1;j<=p[i];j++)
		{
			scanf("%lld",&eff[i][j]);
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=m;j>=w[i];j--)
			for(int k=1;k<=p[i];k++)
				if(j>=k*w[i]) 
				{
					if(dp[j][0]<dp[j-k*w[i]][0]+eff[i][k])
					{
						dp[j][0]=dp[j-k*w[i]][0]+eff[i][k];
						//printf("花费%d时,第%d种魔法升级为%d\n",j,i,k); 
						//
							dp[j][i]=k;
							for(int a=i-1;a>=1;a--)
							dp[j][a]=dp[j-k*w[i]][a];
					}
					if(dp[j-k*w[i]][0]+eff[i][k]==dp[j][0]){
					int sum1=0,sum2=0;
					for(int a=1;a<=n;a++){
						sum1+=w[a]*dp[j][a];
					}
					sum2+=k*w[i];
					for(int a=i-1;a>=1;a--){
						sum2+=w[a]*dp[j-k*w[i]][a];
					}
					if(sum2<sum1){
						dp[j][i]=k;
						for(int a=i-1;a>=1;a--){
							dp[j][a]=dp[j-k*w[i]][a];
						}
					}
				}
					
				}
	for(int i=0;i<=n;i++)
		printf("%d\n",dp[m][i]);
	return 0;
}
/*
3 10
1 3 1 2 2 
2 3 2 4 6 
3 3 2 1 10
*/

T3

解析:把每个有果子的树取出来,就变成了费用为2*(i+j)的多重背包。

   注意事项:

  • dp数组开到N*N
  • 体力最后不能为0
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
#define ll long long
int mp[N][N],n,m,a,b,m1,m2;
ll dp[N];
int p[N*N],w[N*N],cnt,val[N*N];
int main()
{
	scanf("%d%d%d%d",&a,&b,&m1,&m2);
	m2--;
	m=min(m1,m2);
	for(int i=1;i<=a;i++)
		for(int j=1;j<=b;j++)
		{
			scanf("%d",&mp[i][j]);
			w[++cnt]=2*(i+j),val[cnt]=mp[i][j];
		}
	cnt=0;
	for(int i=1;i<=a;i++)
		for(int j=1;j<=b;j++)
		{
			int tmp;
			scanf("%d",&tmp);
			p[++cnt]=tmp;
		}
	for(int i=1;i<=cnt;i++)
		for(int j=m;j>=w[i];j--)
			for(int k=1;k<=p[i];k++)
				if(j>=k*w[i])
				dp[j]=max(dp[j],dp[j-k*w[i]]+k*val[i]);
	printf("%lld",dp[m]);
	return 0;
}
/*
4 4 13 20
10 0 0 0 
0 0 10 0
0 0 10 0
0 0 0 0
1 0 0 0
0 0 2 0 
0 0 4 0
0 0 0 0 
*/

T4

解析:经典题

分类讨论:

  • 只选择当前主件
  • 选择当前主件和第1个附件
  • 选择当前主件和第2个附件
  • 选择当前主件和第1、2个附件
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 3.2e4+5 , M = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,m;
int w[M],v[M],ma[M][3],cnt,cntm,wa[M],va[M]; 
int dp[N],to[N];
signed main(){
	n = read() , m = read();
	int a,b,c;
	for(int i = 1 ; i <= m ; i ++){
		a = read() , b = read() , c = read();
		if(!c)
			to[i] = ++cnt,w[cnt] = a, v[cnt] = b * a;
		else
			 ma[to[c]][ ++ma[to[c]][0] ] = ++cntm , wa[cntm] = a , va[cntm] = b * a;
	}
	for(int i = 1 ; i <= cnt ; i ++){
		for(int j = n ; j >= w[i] ; j --){
			dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
			if(j >= w[i] + wa[ma[i][1]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]] + v[i] + va[ma[i][1]]);
			if(j >= w[i] + wa[ma[i][2]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][2]]] + v[i] + va[ma[i][2]]);
			if(j >= w[i] + wa[ma[i][1]] + wa[ma[i][2]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]-wa[ma[i][2]]] + v[i] + va[ma[i][1]] + va[ma[i][2]]);
		}
	}
	int ans = -INF;
	for(int i = 0 ; i <= n ; i ++)
		ans = max(ans,dp[i]);
//		printf("%d:%d\n",i,dp[i]);
	printf("%d",ans);
	return 0;
}
posted @ 2021-06-10 19:59  conprour  阅读(48)  评论(0编辑  收藏  举报