zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",i,dp[i]);
    	printf("%d",ans);
    	return 0;
    }
  • 相关阅读:
    1105 Spiral Matrix (25分)(蛇形填数)
    1104 Sum of Number Segments (20分)(long double)
    1026 Table Tennis (30分)(模拟)
    1091 Acute Stroke (30分)(bfs,连通块个数统计)
    1095 Cars on Campus (30分)(排序)
    1098 Insertion or Heap Sort (25分)(堆排序和插入排序)
    堆以及堆排序详解
    1089 Insert or Merge (25分)
    1088 Rational Arithmetic (20分)(模拟)
    1086 Tree Traversals Again (25分)(树的重构与遍历)
  • 原文地址:https://www.cnblogs.com/conprour/p/14872587.html
Copyright © 2011-2022 走看看