算法-DP

总结

1.背包问题

1)01背包(物品选 或 不选)

先想二维,再优化为一维。

二维code

#include <iostream>

using namespace std;
const int N = 1010;
int v[N], w[N]; //体积数组v, 价值数组w
int f[N][N]; //二位状态f[i][j]      : 前i个物品,总体积不超过j的最大价值。

int main()
{
    int m, n;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    //f[0][0~m] = 0 //前0个物品,总体积……的最大价值为0,因为没有物品可选
    for(int i = 1; i <= n; i++)
        for(int j = 0; 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]);  //包括第i个物品
        }
        
    cout << f[n][m] << endl;
    return 0;
}

二维数组f[i][j]的数值

优化:一维code

#include <iostream>

using namespace std;
const int N = 1010;
int v[N], w[N]; //体积数组v, 价值数组w
int f[N]; //二位状态f[i][j]      : 前i个物品,总体积不超过j的最大价值。
//滚动数组
int main()
{
    int m, n;
    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 = m; j >= v[i]; j--)
            f[j] = max(f[j], f[j-v[i]] + w[i]);  //包括第i个物品
            
    cout << f[m] << endl;
    return 0;
}

2)完全背包(每个物品 可以选 无限次)

推导过程

朴素版code(状态划分为 【不选】,【选1个】,【选2个】,……直到体积超过可选范围)

#include<iostream>

using namespace std;
const int N = 1010;
//一样的变量
int v[N], w[N];
int f[N][N];

int main()
{
    int n, m;
    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 = 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);
                
    cout << f[n][m] << endl;
            
    return 0;
}

优化版

// 推导过程:
// step1.根据正常【状态划分】推导:
//     f[i][j] = max(f[i-1][j], f[i-1][j-v[i]*1]+w[i]*1, f[i-1][j-v[i]*2]+w[i]*2, f[i-1][j-v[i]*3]+w[i]*3, …………)
//     针对第i件物品:不选        选一件                   选两件                  选三件                  …………
// step2.凑出一个式子,来化简上面这个长式子
//     f[i][j-v[i]]    //我觉得是凑出来的,接下来根据【状态划分】思路对f进行划分
//     f[i][j-v[i]]=max(        f[i-1][j-v[i]],           f[i-1][j-v[i]-v[i]*1]+w[i], f[i-1][j-v[i]-v[i]*2]+w[i]*2, ……)
// 针对第i件物品,总体积不超过j-v[i]:不选(比上面少1个w), 选一件(比上面少1个w),      选两件(比上面少1个w)
// step3.合并上面两式子
//     f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]);
//        说明:  step1的第一项  step2的整体+w[i]
#include<iostream>

using namespace std;
const int N = 1010;
//一样的变量
int v[N], w[N];
int f[N][N];

int main()
{
    int n, m;
    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 = 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]);
            }
                
    cout << f[n][m] << endl;
            
    return 0;
}

一维跟01背包一样 化简下标就可以(只不过01背包是反着写,完全背包是正着写!)

总结:01背包和完全背包 状态转移方程的区别

01:f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);
一维 反着写

完全:f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]);
一维 正着写

完全背包靠那个式子推导出来的,跟当前i有关,所以要正着写

而01背包根据状态划分得到的式子 跟i-1有关,避免使用已经计算过的数值,所以要逆着写


3)多重背包(物品次数给定)

朴素版(暴力求解,枚举选0次、1次、直到k次)

#include<iostream>

using namespace std;
const int N = 110;
int v[N], w[N], s[N];
int f[N][N];
int n, m;

int main()
{   
    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 = 0; j <= m; j++)
            for(int k = 0; k <= s[i] && k * v[i] <= j; k++) //枚举次数,条件:不超过k次,并且体积不大于j
                f[i][j] = max(f[i][j], f[i-1][j-k*v[i]]+w[i]*k);
    
    cout << f[n][m] << endl;
    return 0;
}

优化

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值