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

背包问题

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

输出样例

10

动态规划 -二维数组朴素算法 三层循环

大致思路

数据量最大1000会超时,主要理解思路

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

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

2 第外层循环遍历N件物品,内层循环遍历1~V的整数体积,f[i] [j] 表示前i件物品最近体积为j时的最大价值

3 每件物品可以填充多个,因此需要找出放入1~t/v[i]件i物品时最大体积,三重循环

4 考虑前i件物品时,把前i件物品组合的集合分成2部分,不加i的最大价值和i的最大价值

不加i的最大价值 dp[i-1] [ j-k*v[i]]

i物品的最大价值 k*w[i] (k可以时0个或者多个i物品)

参考程序

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int v[N],w[N];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int dp[N][N];//f[i][j]前i件物品,承重不超过j的最大价值
int main() {
    int N,V;
    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=1;j<=V;j++) {//遍历每个整数的体积
            int t=j/v[i];//背包可以装t个体积为v[i]物品
            for (int k=0;k<=t;k++){//0 1 2 3...t 逐个装入背包
                //类似01背包 装和不装取最大 装包括装1个装2个(包含01背包) 
                //k=0时dp[i-1][j-k*v[i]]+k*w[i] -- dp[i-1][j]
            	dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
			}
        }
	}
    cout<<dp[N][V];//装N件物品在不超过体积V的最大价值 
    return 0;
}

动态规划 -二维数组朴素算法 双重循环优化

大致思路

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

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

2 第外层循环遍历N件物品,内层循环遍历1~V的整数体积,f[i] [j] 表示前i件物品最近体积为j时的最大价值

3 i个物品装入体积j的背包产生的集合分为2部分:1不包括i和2包括1个或多个i

不包括i时

i个背包不超过体积j的最大价值f[i] [j]=f[i-1] [j]
包括i时

可能包括1个i,包括2个i包括3个i...

对应i个物品装入j体积背包最大价值如下1式:

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

又对应i个物品装入j-v体积背包最大价值如下2式:

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

1式每个元素都比2式大w,对应的max也大w

因此f[i] [j] = f[i] [j-v]+w

综合上述不包括i包括i 2种情况,得出完全背包的动态转移方程如下:

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

参考程序

#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][j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
        	}
        }
	}
	
	cout<<f[N][V];
    return 0;
}

一维滚动数组优化

1 二维完全背包问题的等价变形

2 i个物品装入体积j的背包产生的集合分为2部分:1不包括i和2包括1个或多个i

不包括i时

i个背包不超过体积j的最大价值f[i] [j]=f[i-1] [j]

f[i] [j]=f[i-1] [j] 还未变化可以转变为f[j]=f[j] 可以省略

包括i时

可能包括1个i,包括2个i包括3个i...

对应i个物品装入j体积背包最大价值如下1式:

f[i] [j] = max(f[i-1] [j],f[i] [j-v]+w) 此时 f[i] [j-v]是i个物品时体积j-v的最大值,需要提前计算,因此需要从左到右循环

01背包是f[i-1] [j-v] 是i-1个物品时体积j-v的最大值,是i-1个物品的,不能被第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[i];j<=V;j++){//遍历每个整数的体积
            f[j]=max(f[j],f[j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
        }
	}
	
	cout<<f[V];
    return 0;
}

CSP初赛复习-28-动态规划-练习题
https://www.cnblogs.com/myeln/articles/17626730.html

posted @ 2023-08-15 22:46  new-code  阅读(63)  评论(0)    收藏  举报