集合角度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);
        }

 

posted @ 2020-07-21 17:37  龙雪可可  阅读(191)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************