Acwing算法基础课----0/1背包问题的优化

0/1背包问题的优化

[参考]
https://www.acwing.com/solution/content/1374/
优化思路以及代码参考上面这个链接,下面主要记录自己在写代码时的优化步骤

步骤1

首先要把用二维数组实现背包问题的代码写出来

#include
#include

using namespace std;

const int N=1010;
int n,m;
int f[N][N];
int v[N],w[N];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
      cin>>w[i]>>v[i];
                            //i从1开始循环,因为从0开始会发生数组越界,又根据f[i][j]意义,知f[0][0~m]代表从0件物品选
    for(int i=1;i<=n;i++)   //所以知f[0][0~m]=0,默认初始化f[N][N]就是0,所以不用操作
        for(int j=1;j<=m;j++){  //这里j从0开始和从1开始都可以,因为根据递推方程可知j不会发生数组越界的问题
            if(j<w[i]) f[i][j]=f[i-1][j];  //所以j从0开始可以相当于把f[][0]都给赋值为0,从1开始的话就是利用默认初始化f[N][N]就是0。
            else f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i-1][j]);
        }
    cout<<f[n][m]<<endl;
}

步骤二

将二维数组变成一维数组,然后对递归方程进行优化。

for(int i = 1; i <= n; i++) 
    for(int j = m; j >= 0; j--)  
	{								
        if(j < v[i]) 
           f[i][j] = f[i - 1][j];  // 优化前
           f[j] = f[j];            // 优化后,该行自动成立,可省略。
        else    
           f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);  // 优化前
            f[j] = max(f[j], f[j - v[i]] + w[i]);                   // 优化后
   } 

为什么要对对j进行逆序枚举?
可以画一个棋盘格子帮助理解
Acwing算法基础课----0/1背包问题的优化_第1张图片
顺序枚举j是每一行从左到右计算出每个格子的值,没有被计算的格子保留着上一次计算的旧值。也就是i-1那次的值。
所以顺序枚举的问题就是比j小的f[i - 1][j - v[i]]被先更新了f[i][j - v[i]]。
也就是f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i])变成了f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])

步骤三

//第三步:只有当枚举的背包容量 >= v[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]);
}

你可能感兴趣的:(Acwing算法基础,数据结构,算法)