01背包

01背包
题目
有N件物品和一个容量为V的背包。第i件物品的重量是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
物品12345
w2416
v45762
思路
这是最基础的背包问题特点是:每种物品仅有一件,可以选择放或不放。

将前i件物品放入容量为j的背包中,只考虑第i件物品(放或不放),那么前i件物品能获得的最大价值由前i-1件物品能获得的最大值有关。

如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为dp[i-1][j];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,此时能获得的最大价值就是p[i-1][j-w[i]]再加上通过放入第i件物品获得的价值v[i]。

用子问题定义状态:即f[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。
二维状态转移方程是:dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i])。

一维状态转移方程是:dp[j]=max(dp[j],dp[j-w[i]]+v[i])。我在计算dp(i, j)的时候,因为dp(i, j+1..kg)这些状态已经被计算过了,所以意味着dp(i - 1, k),k=j..kg这些值都没有用了——所有依赖于他们的值都已经计算完了。于是它们原有的存储空间都可以用来存储别的东西,所以我就直接就将dp(i, j)的值存在dp(i-1, j)原有的位置上

如果只求最大价值:则下面第一个代码就可以,若还需输出哪些物品被选了,则加一个take[]数组,记录是否被选,见第二个代码

只求最大价值,二维存储
#include<stdio.h>
#define max(a,b) a>b?a:b 

int w[100],v[100],dp[100][100],n,kg;

void dpfind(){
	int i,j;
	for(i=0;i<=n;i++){//背包不能装物品 
		dp[i][0]=0;
	}  
	for(i=0;i<=kg;i++){//背包一个物品也没装 
		dp[0][i]=0;
	}
	for(i=1;i<=n;i++){//前i个物品 
		for(j=1;j<=kg;j++){//容量为j的背包 
			if(j>=w[i]){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
		}
	}	
}

int main(){
	int i,j;
	scanf("%d%d",&n,&kg);
	for(i=1;i<=n;i++){//重量
		scanf("%d",&w[i]);
	}
	for(i=1;i<=n;i++){//价值 
		scanf("%d",&v[i]);
	}
	dpfind();
	printf("%d\n",dp[n][kg]);//输出最大价值 	
}
只求最大价值,一维存储
#include<stdio.h>
#include<string.h> 
#define max(a,b) a>b?a:b 

int w[100],v[100],dp[100],n,kg;

void dpfind(){
	int i,j;
	for(i=1;i<=n;i++){//前i个物品 
		for(j=kg;j>=w[i];j--){//容量为j的背包 
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}	
}

int main(){
	int i,j;
	scanf("%d%d",&n,&kg);
	memset(dp,0,sizeof(dp));
	for(i=1;i<=n;i++){//重量
		scanf("%d",&w[i]);
	}
	for(i=1;i<=n;i++){//价值 
		scanf("%d",&v[i]);
	}
	dpfind();
	printf("%d\n",dp[kg]);//输出最大价值 	
} 
输出被选物品

findtake(){//判断是否被选 
	int i,j=kg;
	for(i=n;i>0;i--){
	    if(dp[i][j]==dp[i-1][j]){
	    	take[i]=0;
		}
		else{
			take[i]=1;
			j=j-w[i];
		}		
	}
}

完整:

#include<stdio.h>
#define max(a,b) a>b?a:b 

int w[100],v[100],dp[100][100],n,kg,take[100];

void dpfind(){
	int i,j;
	for(i=0;i<=n;i++){//背包不能装物品 
		dp[i][0]=0;
	}  
	for(i=0;i<=kg;i++){//背包一个物品也没装 
		dp[0][i]=0;
	}
	for(i=1;i<=n;i++){//前i个物品 
		for(j=1;j<=kg;j++){//容量为j的背包 
			if(j>=w[i]){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
		}
	}	
}

findtake(){//判断是否被选 
	int i,j=kg;
	for(i=n;i>0;i--){
	    if(dp[i][j]==dp[i-1][j]){
	    	take[i]=0;
		}
		else{
			take[i]=1;
			j=j-w[i];
		}		
	}
}

int main(){
	int i,j;
	scanf("%d%d",&n,&kg);
	for(i=1;i<=n;i++){//重量
		scanf("%d",&w[i]);
	}
	for(i=1;i<=n;i++){//价值 
		scanf("%d",&v[i]);
	}
	dpfind();
	printf("%d\n",dp[n][kg]);//输出最大价值 
	
	findtake();
	for(i=1;i<=n;i++){//输出装入背包的物品 
		if(take[i]==1){
			printf("%d ",i);
		}
	}	
} 


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值