dp-背包问题

—01背包 完全背包先学。
代码随想录 笔记dp

1. 01背包问题

N件物品和一个容量为V的背包,每件物品只能使用一次。
求装入哪些物品,使得总价值最大,且不超过背包容量。
输出最大值
N=4,V=5

体积价值
12
24
34
45

dp[n][v] :
解释:v的体积限制下,决策前n个物品后,所获得的最大价值。
定义f[i][j]
表示从前 i 个物品中选择,体积为 j 的时候的最大价值。

一、当前背包余量够放物品 充足
1.放入
放入第n个物品,则产生的价值为
(n-1,V-v[n])状态下的最优值,加上第n个物品的价值 v[n]
即 dp[n-1][V-v[n]]+v[n]
2.不放入
不放入第n个物品,则产生的价值不改变,等于决策前n-1个物品的最优值
即dp[n-1][V]
取max
dp[n][v]=max{dp[n-1][V-v[n]]+v[n],dp[n-1][V]};
二、当前背包容器不够放入最后一个物品
那么dp[n][v]=dp[n-1][v]
111
回溯图解回溯

一维数组优化
在这里插入图片描述

1.1 代码参考

01背包代码参考

#include <iostream>
using namespace std;
 
const int N=1010;
int v[N],w[N],f[N][N];
// v[N] 物品重量,W[N] 物品最大价值
int N,V;//n 物品数量 v 背包容量
 
int main()
{
  scanf("%d%d",&n,&m);
  
  for(int i=1;i<=N;i++) scanf("%d%d",&v[i],&w[i]);
  
  for(int i=1;i<=N;i++)
    for(int j=1;j<=V;j++)
    {
   // if(j>=v[i]){
    //放的下第i件物品
	//f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
	//}else{
	//当不下第i件物品
	//	f[j][j]=f[i-1][j];
	//}
      f[i][j]=f[i-1][j];
      if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
    }    
 
  printf("%d",f[n][m]); 
  return 0;
}

void flashBack(){
	int i=N,j=V;
	// 从最后一个进行回溯
	while(i!=0){
		if(f[i][j]===f[i-1][j]){
			// 说明相等,没有选中第i件物品
			i--;
		}else{
			// 选择了第i件物品
			list.push_back(i);
			// 减去第i件物品的重量空间
			j-=v[i];
			i--;
		}
	}
}


void better_dp(){
	//简化 从后往前遍历
	 for(int i=1;i<=N;i++)
  {  for(int j=V;j>=v[i];j--)
    {
      f[j]=max(f[j],f[j-v[i]]+w[i]);
    }
}


void knapsack(){
	//边界处理
	for(int i=0;i<=V;i++)fi[0][i]=0;
	//状态更新
	for(int i=1;i<=N;i++){
		for(int j=0;j<=V;j++){
			if(j>=v[i]){
				f[i][j]=max(f[i-1][j],f[i-1][j-v[i]+w[i]]);
			}else{
				f[i][j]=f[i-1][j];
			}
		}
	}	
 }
}

状态方程
dp[n][v]:
剩余容量能够放入最后一个物品 :dp[n][v]=max{dp[n-1][V-v[n]]+v[n],dp[n-1][V]};
不够 dp[n][v]=dp[n-1][v]

状态压缩
dp[v]:
剩下的容量能够放入最后一个物品
dp[v]=max{dp[v-w[i]]+c[i],dp[v]} , v-w[i]>=0
不够
dp[v]=dp[v]

2. 多重背包问题

有n件物品,每件物品的重量为 w[i],价值为c[i]。现有一个容量为V的背包。
问如何选取物品放入背包,使得背包内的物品的总价值最大。其中每件物品有 s[i] 件。
输入数据描述

物品重量价值数量
1322
2432
3221
4534

当背包的容量V=10时,所能产生的最大价值?

2.1 朴素方法

对相同物品张开

物品重量价值数量
1321
1321
2431
2431
3221
4531
4531
4531
4531

转化为 01 背包问题,数量已经没用了
物品一共有 ∑s[i],共有∑s[i]行。时间复杂夫 O(V*∑s[i])

2.2 二进制优化算法

多重背包算法代码-参考
在这11述
二进制拓展行后的

物品重量价值数量
1321
2321
3431
4431
5221
6531
71061
8531
//多重背包问题
 # include<stdio.h>
        #include<iostream> using namespace std;

        const int MAXN=10010;//定义最大物品数量
        const int MAXV=10010;//定义最大背包容量
        int N;// 物品数量 行拓展后的
        int V;//背包容量
        int w[MAXN];//储存每件物品的重量w[i]
        int c[MAXN];//储存每件物品的价值c[i]
        int s[MAXN];//储存每件物品的数量s[i]
        int dp[MAXV];//滚动dp数组

        //用滚动dp数组求解
        void knapsack() { //边界处理
        for (int i = 0; i <= V; i++) 
        dp[i] = 0;
        //状态更新
        for (int i = 1; i <= N; i++) {
        //倒序枚举v(v—0)
        for (int v = V; v >= w[i]; y--)
      	 dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
        }
     }
    int main(){
    	//实际上每件物品的重量和价值
    	int tempw[MAXN],int tempc[MAXN];
    	int k=0;// 存储展开后的标号
    	cin>>N>>V;//物品个数和最大背包容量
    	//读取tempw[i] 实际的物品重量读入
    	for(int i=1;i<N;i++){
			cin>>tempw[i];
		}
		// 读取物品 的价值
		for(int i=1;i<N;i++){
			cin>>tempc[i];
		}
		// 利用二进制优化方法拓展行
		//读入物品件数
		for(int i=1;i<N;i++){
			cin>>s[i];
		//每读取一次进行一次行拓展
		//第一行1 二行2个 三行4个  2^n
		// j=1 从第一行开始 j=1表示第一行数量为1 2^0
		for(int j=1;j<s[i];j*=2){
			k++;//k新数组
			w[k]=tempw[i]*j;// 拓展后是重量
			c[k]=tempc[i]*j;//拓展后的价值
			s[i]-=j;
			}
		// 计算剩下的 s[i];
		if(s[i]!=0){
		k++;
		w[k]=s[i]*tempw[i];
		c[k]=tempc[i]*s[i];
		}
	 }//endfor
	 N=k;//实行拓展后实际物品件数
	 knapsack();
	 cout<<dp[V];
	 
   }

3.完全背包问题

完全背包代码

3.1公式推导

格式

f[i , j ] = max( f[i-1,j] , f[i- 1,j - v[i]+ w[i] , f[i - 1,j-2 * v[i]]+2 * w[i] , f[i -1,j - 3 * v[i]]+3 * w[i] , …)
f[i , j - v[i]]= max( f[i - 1,j - v[i]] , f[i - 1,j - 2 * v[i]] + w[i] , f[i - 1,j- 3 * v[i]]+2 * w[i] , …)
f[i][j-v[i]]+w=f[i][j]

ss

3.2 滚动数组

ssa

4.总结

aa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值