0
点赞
收藏
分享

微信扫一扫

DP 完全背包问题

蓝哆啦呀 2022-03-18 阅读 48

 

(一)

依然是按照如下图进行分析

其实关键就是怎么划分集合

我们来注意现在的条件,每个物品都可以无限取

所以我们在划分集合的时候,要保证所有情况都能遍历过

所以第i种物品取1~k个的情况都要包含

注意:

现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的,对第i层的更新

是从1~k个不同的选择 去取其中的最大值

而01背包是从上一个,即f[i-1][j]去更新的,是对第i-1层的更新

不妨这样来划分集合

于是我们得到以下代码

此时的f[i][j]就是取f[i][j-k*v[i]]+k*w[i]的最大值  

就是寻找在体积是j-v[i]的状态下价值的最大值

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

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

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
    
    for(int i=1;i<=n;i++) 
       for(int j=0;j<=m;j++)
          for(int k=0;k*v[i]<=j;k++)
             f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
             
    printf("%d",f[n][m]);
    return 0;
}

(二)优化

我们看到三层循环太慢了,所以做优化

依然是从集合划分去看 我们看f[i][j]是怎么更新的

 那现在先来拆解代码

k层循环去除后
for(int i=1;i<=n;i++)
   for(int j=0;j<=m;j++)
   {
        if(j-v[i]>=0) f[i][j]=max(f[i][j],f[i][j-v[i])
        else f[i][j]=f[i-1][j];
   }

因为现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的

如此仿照将其变成一维的

现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的,对第i层的更新

所以就遍历的时候就不用担心j-k*v[i]先于j 导致与我们初始对f[i][j]的定义产生矛盾

即保证了逻辑的合理性

现在代码就是

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1010;
int n,m;
int v[N],w[N];
int f[N];//最大值


int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++) scanf("%d%d",&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];
    return 0;
}

其实这个代码和01背包很像,区别就在于j是否要逆序遍历

而决定这一点的就是我们对于集合的划分方式,对f[i][j]的定义

题目来源acwing 

模板来源:闫学灿
链接:https://www.acwing.com/activity/content/code/content/57785/
来源:AcWing

举报

相关推荐

0 条评论