CSP初赛复习-28-动态规划-01背包

背包问题

01 背包

每种物品只有一个,每件物品只有选与不选两种状态

完全背包

每种物品有无限个

多重背包

每种物品有多个,并且多种物品数量不完全相同

分组背包

按组打包,每组最多一个

01背包

问题描述

现有 N件物品和一个容量为V的背包,第 i件物品的重量是 v[i],价值是 w[i],在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值

输入格式

第一行两个整数,N和V用空格隔开,分别表示物品数量和背包容积 ( 0<=n,m<=1000)

接下来有N行每行两个整数,v[i],w[i]用空格隔开,分别表示第i件物品的体积和价值 ( 0<=v[i],w[i]<=1000)

输出格式

输出一个整数,表示最大价值

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例

8

动态规划 -二维数组朴素算法

大致思路

1 问题分解,求N件物品不超过容积V的最大价值,转化为f[i] [j] i件物品不超过体积j的所有集合,所有状态

转换为求f[i] [j] i件物品不超过体积j的最大价值(1<=i<=N,0<=j<=V)

2 f[i] [j] 每增加一个物品时,比如从i-1到i,对原有集合的影响分为2部分,

对原有集合最大价值无影响 f[i-1] [j] 比如价值太小 或体积超大

对原有集合最大价值有影响 f[i-1] [j-v[i]]+w[i],加入i物品可看做i本身价值+去除i不超过去除i体积v[i]的最大价值

对上面2部分取最大值

3 所以状态转移方程为

f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])

4 最优子结构

原问题的最优解包含子问题最优解 f [i] [j] 可以通过前面子问题的解计算出来

f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])

5 重叠子问题

在计算子问题过程中,会出现很多重复使用更小子问题的解

6 无后效性

求解每个子问题的最优解只依赖前面计算好的更小子问题的解,不依赖后面更大问题的解

f[i] [j]不会依赖f[i+1] [j+1]

参考程序

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int N,V;
int v[maxn],w[maxn];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int f[maxn][maxn];//f[i][j]前i件物品,承重不超过j的最大价值

int main(){
    cin>>N>>V;//N件物品 V背包容积
    for (int i=1;i<=N;i++){//输入每件物品的体积和价值
    	cin>>v[i]>>w[i];	
	}
    for (int i=1;i<=N;i++){//n件物品
    	for (int j=0;j<=V;j++){//遍历每个整数的体积
            f[i][j]=f[i-1][j];//不选择i物品时和f[i-1][j]最大价值相同
            //j<v[i]时f[i-1][j-v[i]]为空集,无意义
			if (j>=v[i]){
                //选择i时,可看做除去i的最大价值(f[i-1][j-v[i]])+加上i的价值w[i]
            	f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
        	}
        }
	}
    cout<<f[N][V]<<endl;//N件物品装入背包不超过V体积最大价值
    return 0;
}

一维滚动数组优化

大致思路

1 由01背包二维数组状态转移方程可知

f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])

二维表格中,第i行只和i-1行有关

计算第j列只与j列前面的列(j-v[i])有关

因此,可以使用一维数组替换二维数组进行优化,可以提供处理效率

2 可以从右向左更新第i行的数,未更新的左边的列,则是第i-1行对应的数

3 对体积进行遍历时需要保证从大到小

for (int j=V;j>=0;j--)

4 求除去i本身背包可装的最大价值f[j-v[i]]时,需要保证j>v[i]

参考程序

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int N,V;
int v[maxn],w[maxn];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int f[maxn];//f[j]承重不超过j的最大价值

int main(){
    cin>>N>>V;//N件物品 V背包容积
    for (int i=1;i<=N;i++){//输入每件物品的体积和价值
    	cin>>v[i]>>w[i];	
	}
    for (int i=1;i<=N;i++){//N件物品
    	for (int j=V;j>=0;j--){//遍历V~0每个整数的体积
			if (j>=v[i]){//j<v[i]为空集,无意义
                //选择i时,可看做除去i的最大价值(f[j-v[i]])+加上i的价值w[i]
            	f[j]=max(f[j],f[j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
        	}
        }
	}
    cout<<f[V]<<endl;//N件物品装入背包不超过V体积最大价值
    return 0;
}
posted @ 2023-08-14 23:36  new-code  阅读(88)  评论(0)    收藏  举报