背包DP

01 背包

\(N\) 件物品和一个容量为 \(M\) 的背包。第 \(i\) 件物品的重量是 \(W_i\),价值是 \(D_i\)。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

设DP状态为\(f_{i,j}\)表示在只能放前\(i\)个物品,容量为\(j\)的价值情况

考虑已经放了\(i-1\)个物品背包剩余容量为\(j\),对于第\(i\)个物品在不放入背包的情况下最大价值状态为\(f_{i-1,j}\)
反之\(f_{i-1,j - v_i} + w_i\)

可得状态转移方程

\[\huge f_{i,j} = max(f_{i-1,j},f_{i-1,j-v_i} + w_i) \]

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]);
        }

观察方程可以发现\(f_{i,j}\)之用到了\(f_{i-1,j}\)这一层,且\(j <= j - v[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])
        } 

乍一看好像是对的

其实

ii - 1决定

ji - 1j or j - v[i]决定

j < j - 1j -->是从小往大枚举j,f[i - v[i]]
可能会在第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]);
       return 0;

完全背包

有$ N $种物品和一个容量是 \(V\) 的背包,每种物品都有无限件可用。
\(i\) 种物品的体积是 \(v_i\),价值是 \(w_i\)
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

设DP方程\(f_{i,j}\)下每件物品选\(k\)

状态转移方程为

\[\mathcal{ \huge f_{i,j} = f_{i-1,j-k \times v_i}+ k \times w_i} \]

我们把方程展开

\[\large f_{i,j} = max(f_{i-1,j},f_{i-1,j-v}+w,f_{i-1,j-2v}+2w ,f_{i-1,j-3v}+3w \dots) \]

\[\large f_{i,j -v} = max(f_{i-1,j-v},f_{i-1,j-2v}+w ,f_{i-1,j-3v}+2w \dots) \]

\[\large f_{i,j -2v} = max(f_{i-1,j-2v} ,f_{i-1,j-3v}+w \dots) \]

\[\large f_{i,j -3v} = max(f_{i-1,j-3v} \dots) \]

\[\large max(f_{i-1,j-v}+w,f_{i-1,j-2v}+2w ,f_{i-1,j-3v}+3w \dots) = f_{i,j -v} +w \]

\[\large f_{i,j} = max(f_{i-1,j},f_{i,j-v} + w) \]

朴素

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1010;
int n,m;
int w[N],v[N];
int f[N][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 = 0 ; j <= m ; j ++ )
            for(int k = 0 ; k * v[i] <= j ; k ++ )
                f[i][j] = max(f[i][j],f[i-1][j - v[i] * k] + w[i] * k);
    printf("%d",f[n][m]);

}

二维

#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 1010;
int n,m;
int v[N],w[N],f[N][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 = 0 ; 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]);
        }
        
        printf("%d",f[n][m]);
    
}

一维

#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 1010;
int n,m;
int v[N],w[N],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]);
        }
        
        printf("%d",f[m]);
    
}

多重背包

\(N\) 种物品和一个容量是 \(V\) 的背包。
\(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)

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

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 25000;

int n,m,f[N],w[N],v[N];

int main()
{
    cin >> n  >> m ; 

    int cnt  = 0 ;
    for(int i = 1 ; i <= n ; i ++ )
    {
        int a,b,c;
        cin >> a >> b >> c;
        int k = 1 ;
        while(k <= c)
        {
            v[++cnt] = a * k ;
            w[cnt] = b * k ;
            c -=  k;
            k *= 2 ;
        }
        if(c > 0)
        {
            v[++cnt] = a * c ;
            w[cnt] = b * c;
        }
    }
    
    n = cnt;

    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] );
        
    cout << f[m] << endl;
}

分组背包


#include <iostream>
#include <algorithm>

using namespace std;
const int N = 110;
int f[N];
int v[N][N],w[N][N],s[N];
int n,m;
int main()
{
    cin >> n >> m; 
    for(int i = 1 ;i <= n ; i++ )
    {
        cin >> s[i];
        for(int j = 1 ;j <= s[i] ; j++ )
            cin >> w[i][j] >> v[i][j];
    }
    
    for(int i  = 1 ; i <= n ; i ++)
        for(int j = m ;j >= 0 ; j-- )
            for(int k = 0 ; k <= s[i] ; k ++ )
            if(w[i][k] <= j)
            f[j] = max(f[j],f[j - w[i][k]] + v[i][k]);
            cout << f[m];
            return 0;
    
}
posted @ 2022-07-12 22:19  Erfu  阅读(11)  评论(0编辑  收藏  举报