01背包问题C++

1.问题简述:

有 N 件物品和一个容量是 V 的背包,每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi 。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大

2.朴素解法及优化:

定义状态 f[i][j] 表示:前 件物品 当体积不超过 j 时的所有选法的集合

状态方程 f[i][j] 的状态转移关键在于 第 i 件物品选或不选

  • 不选第 i 件时

f[i][j] = f[i - 1][j];

  • 选第 i 件时

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

  • 二维朴素写法:
int f[N][N];
int v[N], w[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];// 第 i 个物品一定可以不选,但不一定可以选
            if (j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
    }

    cout << f[n][m] << endl;
}
  • 一维优化:

 01背包优化的实质是 对代码的等价替换

(这里我们先将优化 前后 的主要代码放出)

//优化前
    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 - 1][j - v[i]] + w[i]);
        }
    }
//优化后
    for(int j = m; j >= v; j--)
         
        f[j] = max(f[j], f[j - v] + w);


我们的思路是:

  1. 先将二维状态方程 f[i][j] 直接去掉 i ,转换未处理完全的一维状态方程 f[j]
  2. 再根据转换后的 f[j] 与 转换前的 f[i][j] 有什么差异
  3. 最后根据差异,将未处理完全的 f[j] 完善。

1.不考虑影响 直接去掉 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 - 1][j - v[i]] + w[i]);
        }   //将外层循环i去掉,并将状态方程f[i][j]转移为f[j]
            //若不考虑影响,直接变化
            //变化前 :f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
            //变化后: f[j]    = max(f[j],    f[j - v]           + w);
    }

2.我们试着分析一下变化后的该代码段的意义以及差异:

    for (int i = 1; i <= n; i++) 
    {
        for (int j = 1; j <= m; j++) 
        {
            if (j >= v[i])     f[j]   =   max( f[j],      f[j - v]  +  w );
        }                   // f[i][j]         f[i-1][j]  f[i][j - v]
                     //优化前     f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
      
       
    }              //第一个f[j] 是在第i层计算出 所以实质为f[i][j], 
                   //第二个f[j] 是在第i - 1层计算出的 所以实质为f[i - 1][j]

                   //而f[j - v], 由于第二层循环是从小到大 易知j - v < j;
                   //因此f[j - v] 早于f[j]计算出 所以实质为f[i][j - v]
                   //但优化前f[i - 1][j - v[i]] 可知f[j - v] 晚于f[j]计算出
     
 

3.处理:

循环倒序进行, 从而使f[j - v] 早于f[j]计算出

    for(int j = m; j >= v; j--)
         
        f[j] = max(f[j], f[j - v] + w);


你可能感兴趣的:(c++,c++,蓝桥杯,c语言,动态规划)