集合角度DP分析
动态规划(DP,Dynamic Programming)
一、状态表示(f[i][j]表示什么?):
1、集合:所有只考虑前 i 个物品,且总体积不超过 j 的选法的集合。
2、属性:最大值(Max),还有最小值(Min),方案数。
二、状态计算(f[i][j]怎么算出来?):
1、所有不选第 i 个物品的方案:
1 ~ i <= j, ------>>> 1 ~ i - 1 <= j,f[i - 1, j];
2、所有选择第 i 个物品的方案:
因为第 i 个物品必选,然后前 i - 1个物品任意选择。
1 ~ i - 1 + i <= j,1 ~ i - 1 <= j - vi ------>>> f[i - 1][j - vi] + w[i]
然后在选 i 和不选 i 两种方案中取max :f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
DP优化:只是对代码做等价变形。
优化方式:空间优化,二维优化成一维,f[j] = min(f[j], f[j - v[i]] + w[i]),然后第二层体积循环从大到小枚举:for(int j = V; j >= v[i]; j --),这样就可以保证在枚举 i 这一层物品的时候,f[j - v[i]]没有更新,用的还是上一层的 f[i - 1][j - v[i]]的体积。
如果改成一维数组后第二层体积循环依然是从小到大枚举的话:for(int j = 0; j <= m; j ++),因为 f[j - v[i]]体积是枚举过的,在这一层得到更新,所以对应的一定是这一层 i 时候的 j - v[i] 即:f[i][j - v[i]]。
f[j - v[i]] + w[i]取决于它是在第 i 层算出来的还是第 i - 1层算出来的,此时的f[j - v[i]]一定是第 i 层的。
n = 4, V = 5,第1 - 4件物品的体积和价值分别是:
1 2
2 4
3 4
4 5
体积 编号 |
0 | 1 | 2 | 3 | 4 | 5 |
第0个物品 | 0 | 0 | 0 | 0 | 0 | 0 |
第1个物品 | 0 | 2 | 2 | 2 | 2 | 2 |
第2个物品 | 0 | 2 | 4 | 6 | 6 | 6 |
第3个物品 | 0 | 2 | 4 | 6 | 6 | 8 |
第4个物品 | 0 | 2 | 4 | 6 | 6 | 8 |
for(int i = 1; i <= n; i ++) for(int j = 0; j <= m; 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]); }
for(int i = 1; i <= n; i ++) for(int j = m; j >= v[i]; j --) { f[j] = max(f[j], f[j - 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]); }
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]);
三、石子合并
#include <iostream> using namespace std; const int N = 310; int n, s[N], f[N][N]; int main() { cin >> n; for(int i = 1; i <= n; i ++) cin >> s[i], s[i] += s[i - 1]; for(int len = 2; len <= n; len ++) for(int i = 1; i + len - 1 <= n; i ++) { int j = i + len - 1; f[i][j] = 1e9; for(int k = i; k < j; k ++) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); } cout << f[1][n] << endl; }
四、最长公共子序列
f[i][j] = max(f[i - 1][j - 1], f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + 1) ------>>> f[i][j] = max(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + 1)
for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { f[i][j] = max(f[i - 1][j], f[i][j - 1]); if(A[i] == B[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1); }