DP背包问题

1、01背包问题

 朴素做法

#include<iostream>
using namespace std;
int n,V;
const int N = 1010;
int f[N][N],v[N],w[N];
int main(){
    cin >> n >> V;
    for(int i = 1;i <= n;i++)
    {
        cin >> v[i] >> w[i];
    }
    
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= V;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]);
        }
    cout << f[n][V];
    return 0;
}

优化后  二维优化为一维重要的是做好方程的等价变换

#include<iostream>
using namespace std;
int n,V;
const int N = 1010;
int f[N],v[N],w[N];
int main(){
    cin >> n >> V;
    for(int i = 1;i <= n;i++)
    {
        cin >> v[i] >> w[i];
    }
    
    for(int i = 1;i <= n;i++)
        for(int j = V;j >= v[i];j--)
             f[j] = max(f[j],f[j - v[i]] + w[i]);
       
    cout << f[V];
    return 0;
}

要注意f[][]的定义,DP问题都是根据定义出发写出相应的状态转移方程。

2、完全背包问题

  朴素做法

#include<iostream>
using namespace std;
const int N = 1010;
int f[N][N],v[N],w[N];
int m,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++)
        {
            for(int k = 0;k * v[i] <= j;k++)
                f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + w[i] * k);
            }
    cout << f[n][m];
    return 0;
}

优化后  方程变量代换优化

#include<iostream>
using namespace std;
const int N = 1010;
int f[N][N],v[N],w[N];
int m,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];
                if(j >= v[i]) f[i][j] = max(f[i][j],f[i][j - v[i]] + w[i]);
            }
    cout << f[n][m];
    return 0;
}

再次优化后 二维变一维

#include<iostream>
using namespace std;
const int N = 1010;
int f[N],v[N],w[N];
int m,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 = v[i]; j <= m;j++)
             f[j] = max(f[j],f[j - v[i]] + w[i]);
            
    cout << f[m];
    return 0;
}

3、多重背包问题

 本质上为完全背包问题

朴素做法

#include<iostream>
using namespace std;

const int N = 110;
int m,n,v[N],w[N],s[N],f[N][N];
int main(){
    int m,n;
    cin >> n >> m;
    for(int i = 1;i <= n;i ++){
        cin >> v[i] >> w[i] >> s[i];
    }
  
    for(int i = 1;i <= n;i++)
        for(int j = 1; j<= m;j++)
            for(int k = 0;k * v[i] <= j && k <= s[i];k++)
                f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + k * w[i]);
                
    cout << f[n][m];
    return 0;
}

4、分组背包问题

 

#include<iostream>
using namespace std;
const int N = 110;
int m,n,v[N][N],w[N][N],f[N][N],s1[N];

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        int s;
        cin >> s;
        s1[i] = s;
        for(int j = 1;j <= s; j++){
            cin >> v[i][j] >> w[i][j];
            }
    }
    //f[i][j] = max(f[i- 1][j],f[i - 1][j - v[i]])
    for(int i = 1;i <= n;i++)  
        for(int j = 1;j <= m;j++){
            f[i][j] = f[i - 1][j];  //这个要放在第二个循环里,不能放在第三个循环。易错!
            for(int k = 1;k <= s1[i];k++)
                  if(j >= v[i][k]) f[i][j] = max(f[i][j],f[i - 1][j - v[i][k]] + w[i][k]);
        }        
    cout << f[n][m];
    return 0;
}

注意易错点。

优化后 二维变一维 注意j从大到小遍历。保证f[j - v[i][k]] 的含义为上一层的值,即f[i - 1][j - v[i][k]]

#include<iostream>
using namespace std;
const int N = 110;
int m,n,v[N][N],w[N][N],f[N],s1[N];

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        int s;
        cin >> s;
        s1[i] = s;
        for(int j = 1;j <= s; j++){
            cin >> v[i][j] >> w[i][j];
            }
    }
    
    for(int i = 1;i <= n;i++)  
        for(int j = m;j >= 0;j--){
            for(int k = 1;k <= s1[i];k++)
                  if(j >= v[i][k]) f[j] = max(f[j],f[j - v[i][k]] + w[i][k]);
        }        
    cout << f[m];
    return 0;
}

5、总结

        背包问题f[i][j]的含义为前i个物品中体积为j时的最大价值。

        状态转移方程 划分状态时主要思路是分为是否选第i个物品分情况讨论。找出最大值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值