背包问题(多重背包+0-1背包)

一:0-1背包问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>
const int maxn=100;
using namespace std;
int w[maxn];
int p[maxn];
int x[maxn];
int c[maxn][maxn];
int kanpsack_1(int n,int m){
	for(int i=0;i<=m;i++){
		c[0][i]=0;
	}
	for(int j=0;j<=n;j++){
		c[j][0]=0;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(j<w[i]){
				c[i][j]=c[i-1][j];
			}else
			{
				c[i][j]=max(c[i-1][j-w[i]]+p[i],c[i-1][j]);
			}
		}
	}
	int j=m;
	for(int i=n;i>0;i--){
		if(c[i][j]>c[i-1][j]){
			x[i]=1;
			j-=w[i];
		}else
		{
			x[i]=0;
	    }
	}
	return c[n][m];
}
int main(){
	int n,W;
	int we;//总重量 
	cin>>n;
	cin>>we;
	for(int i=1;i<=n;i++){
		cin>>w[i];
	} 
	for(int i=1;i<=n;i++){
		cin>>p[i];
	}
	int WE=kanpsack_1(n,we);
	for(int i=1;i<=n;i++){
		cout<<x[i]<<" ";
	}	
	cout<<endl;
	cout<<WE<<endl;
	return 0;
}
/*
5
10
2 2 6 5 4
6 3 5 4 6
*/
深搜0-1背包:记忆化搜索
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int value[1005];
int valume[1005];
int dp[1005][1005];
int n,v;
int dfs(int index,int c){
	int res=0;
	if(dp[index][c]>=0)return dp[index][c];
	if(index>n)return dp[index][c]=0;
	if(c<valume[index]){
		res=dfs(index+1,c);
	}else{
		res=max(dfs(index+1,c),dfs(index+1,c-valume[index])+value[index]);
	}
	dp[index][c]=res;
	return res;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n>>v;
		for(int i=1;i<=n;i++){
			cin>>value[i]; 
		}
		for(int i=1;i<=n;i++){
			cin>>valume[i];
		}
		int sum=dfs(1,v);
		cout<<sum<<endl;
	}
	return 0;
}

非记忆化深搜
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int value[1005];
int valume[1005];
int n,v;
int dfs(int index,int c){
	int res=0;
	if(index>n)return 0;
	if(c<valume[index]){
		res=dfs(index+1,c);
	}else{
		res=max(dfs(index+1,c),dfs(index+1,c-valume[index])+value[index]);
	}
	return res;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n>>v;
		for(int i=1;i<=n;i++){
			cin>>value[i]; 
		}
		for(int i=1;i<=n;i++){
			cin>>valume[i];
		}
		int sum=dfs(1,v);
		cout<<sum<<endl;
	}
	return 0;
}

在这里插入图片描述
二:多重背包:
思路:将多重背包转换为0-1背包。
状态转移方程为 dp[i+1][j]=max(dp[i][j-w[i]*k])+v[k] k=(0…g[i]);g[i]表示物品i共有几个。

方法一:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int W,n;
int w[105],v[105],g[105];
int dp[205][20005];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&W,&n);
        
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&w[i],&v[i],&g[i]);//分别表示物品的重量,价值,这种物品的数量
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<=W;j++){
                dp[i+1][j]=dp[i][j];
                for(int k=1;k<=g[i];k++)
                    if(j>=k*w[i])    dp[i+1][j]=max(dp[i+1][j],dp[i][j-w[i]*k]+v[i]*k);
            }
        printf("%d\n",dp[n][W]);
    }
    return 0;
}


方法二:转化为01背包 
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{
	int cost;
	int weight;
	int nums;
}num[1000005];
int sum_cost,kind;
int dp[1005];
int main(){
	int t;
	cin>>t;
	while(t--){
		int cnt=0;
		cin>>sum_cost>>kind;
		for(int i=1;i<=kind;i++){
			int x,y,z;//分别表示物品的重量,价值,这种物品的数量
			cin>>x>>y>>z;
			for(int j=1;j<=z;j++){
				num[cnt].cost=x;
				num[cnt++].weight=y;
			}
		}
		for(int i=0;i<cnt;i++){
			for(int j=sum_cost;j>=num[i].cost;j--){
				
				dp[j]=max(dp[j],dp[j-num[i].cost]+num[i].weight);

			}
		}
		cout<<dp[sum_cost]<<endl;
	}
	return 0;
}

方法三:记忆化搜索 
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{
	int cost;
	int weight;
	int nums;
}num[105];
int sum_cost,kind;
int dp[105][10005];
int solve(int index,int sumcost){
	int temp;
	if(dp[index][sumcost]>0)return dp[index][sumcost];
	if(index>=kind||sumcost==0)return dp[index][sumcost]=0;
	else if(num[index].cost>sumcost){
		temp=solve(index+1,sumcost);
	}else{
		for(int k=0;k<=num[index].nums&&k*num[index].cost<=sumcost;k++){
			temp=max(solve(index+1,sumcost),solve(index+1,sumcost-num[index].cost*k)+num[index].weight*k);
		}
	}
	dp[index][sumcost]=temp;
	return temp;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>sum_cost>>kind;
		for(int i=1;i<=kind;i++){
			cin>>num[i].cost>>num[i].weight>>num[i].nums;
		}
		int sum=solve(1,sum_cost);
		cout<<sum<<endl;
	}
	return 0;
}
方法四:转换为二进制方法
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int W,n;
int w[2005],v[2005];
int dp[20005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&W,&n);
        int cnt=0;
        int a,b,c;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);//分别表示物品的重量,价值,这种物品的数量
            int e=1;
            while(e<c)
            {
                w[cnt]=a*e;
                v[cnt]=b*e;
                cnt++;
                c-=e;
                e*=2;
            }
            w[cnt]=a*c;
            v[cnt]=b*c;
            cnt++;
        }
        
        for(int i=0;i<cnt;i++)
            for(int j=W;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        printf("%d\n",dp[W]);
    }
    return 0;
}

三:回溯法求解0-1背包
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxx=105;
const int inf=0x3f3f3f3f;
int w[maxx];//物品的重量 
int v[maxx];//物品的价值 
int cp;//当前的价值 
int cw;//当前的重量 
int c;//重量的上限 
int bestp;//最后的最大价值 
int bestx[maxx];//记录被装入背包的物品 
int x[maxx];//记录当前装入背包的物品 
int n,m;
void init(){
	cw=0;cp=0;;
	memset(w,0,sizeof(w));
	memset(v,0,sizeof(v));
	memset(bestx,0,sizeof(bestx));
}
int Bound(int i){//计算上界 ,存在比当前可能的更优解 
	int cleft=c-cw;
	int b=cp;
	while(i<=n&&w[i]<=cleft){
		cleft-=w[i];
		b+=v[i];
		i++;
	}
	if(i<=n){
		b+=v[i]/w[i]*cleft;
	}
	return b;
}
void BackTrack(int t){
	if(t>n){
		for(int j=1;j<=n;j++){
			bestx[j]=x[j];
		}
		bestp=cp;
		return ;
	}
	if(cw+w[t]<=c){
		x[t]=1;
		cw+=w[t];
		cp+=v[t];
		BackTrack(t+1);//进入左子树 
		cw-=w[t];
		cp-=v[t];
	}
	if(Bound(t+1)>bestp){//进入右子树 
		x[t]=0;
		BackTrack(t+1);
	} 
}
int main(){
	while(scanf("%d %d",&n,&c)!=EOF){
		for(int i=1;i<=n;i++){
			scanf("%d",&w[i]);
		}
		for(int i=1;i<=n;i++){
			scanf("%d",&v[i]);
		}
		BackTrack(1);
		cout<<"最大价值: "<<bestp<<endl; 
		cout<<"可以被装入的物品: "; 
		for(int i=1;i<=n;i++){
			if(bestx[i]==1){
				cout<<i<<" ";
			} 
		}
		cout<<endl; 
	}
	return 0;
}

在这里插入图片描述

在这里插入图片描述

这是我自己在做题中总结出来的,如果存在什么问题或者错误,请指出!谢谢!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值