动态规划之完全背包【闫氏DP分析法】(联系01背包附图剖析原理)

动态规划之01背包【从暴力到优化】<—点此进入

先给出完全背包的一个经典基础例题

在这里插入图片描述

可以发现,完全背包和01背包的题目很相似。其实优化处理后,它们的代码也很相似,但是其中蕴含的道理大相径庭。

两者的状态转移方程:

  1. 01背包: f[i][j] = max( f[i-1][j], f[i-1][j-v]+w )
  2. 完全背包: f[i][j] = max( f[i-1][j], f[i][j-v]+w )

相似体现在:1.代码 —> 01背包优化后,第二重循环的 j 由从大到小改为从小到大之后,就能直接应用AC。2.原理 —> 状态表示(往下看)并无大异,大差不离。

两者在题目中的不同主要体现在:01背包每种物品只能用一次,而完全背包的每种物品都有无限件可以使用。

而他们的内在区别主要体现在:状态计算,即集合的划分(在01背包里,是以第 i 个物品选或者不选为界分成两个集合; 而在完全背包里,因为第 i 个物品有无限件可以选择,所以在枚举时,要划分成若干个子集,而不是两个子集)。

完全背包的步骤(结合下方图片理解):
1.状态表示:化零为整,用一个状态表示一类东西。需要搞清楚两件事:
第一个是 f(i,j) 表示哪个集合
第二个是 f(i,j) 这个集合存的是哪个数、哪种属性
2.状态计算:
枚举 f(i,j) 里所有选择了 0~k 个物品的方案数的集合

在这里插入图片描述
在这里插入图片描述
状态转移方程推导完毕!!!步骤讲解完毕!!!下面上朴素做法及优化做法的code QAQ

// 朴素做法
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int n,m;
int v[N],w[N];
int f[N][N]; 

int main()
{
    
    
	cin>>n>>m;
	for(int i=1; i<=n; i++)  cin>>v[i]>>w[i];
	
	for(int i=1; i<=n; i++)
	    for(int j=1; j<=m; j++) 
	    {
    
    
	    	f[i][j] = f[i-1][j];
	    	if(j>=v[i])  f[i][j] = max(f[i][j], f[i][j-v[i]+w[i]]);
		}
	
	cout<<f[n][m]<<endl;
	return 0; 
} 

//优化做法
//优化成一维的之后,对于体积会有限制
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int n,m;
int v[N],w[N];
int f[N]; 

int main()
{
    
    
	cin>>n>>m;
	for(int i=1; i<=n; i++)  cin>>v[i]>>w[i];
	
	for(int i=1; i<=n; i++)
	    for(int j=v[i]; j<=m; j++) 
	        f[j] = max(f[j], f[j-v[i]]+w[i]);
	
	cout<<f[m]<<endl;
	return 0; 
} 

猜你喜欢

转载自blog.csdn.net/Luoxiaobaia/article/details/108943617