动态规划[ part 1]

1. 经典模型

1.1 01背包

dp[i][j] 表示前i件物品 装 体积不超过j的 最大价值
dp[i][j] = max(dp[i-1][j] , dp[i-1][j - v[i]] + w[i])

1.1.1 二维空间

#include<iostream>

using namespace std;

const int N = 1010;

int dp[N][N];
int n ,m;
int v[N],w[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 ++) // 0件物品 最大值 必为0
        for(int j = 0 ; j <= m ; j ++) // dp[i][j] 表示从前i件物品中装入体积不超过j的最大价值
            if(j >= v[i])
                dp[i][j] = max(dp[i-1][j] , dp[i-1][j - v[i]] + w[i]);
            else dp[i][j] = dp[i-1][j];
    
    cout << dp[n][m] << endl;
}

1.1.2 一维空间(空间优化)

  • 滚动数组
  1. 01背包优化后,体积倒序
#include<iostream>

using namespace std;

const int N = 1010;

int dp[N];
int n ,m;
int v[N],w[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 = m ; j >= v[i] ; j --)
                dp[j] = max(dp[j] , dp[j - v[i]] + w[i]);
    
    cout << dp[m] << endl;
}

1.2 完全背包

1.2.1 枚举 + dp

#include<iostream>

const int N = 1010;

using namespace std;

int n , m;
int v[N] , w[N];
int dp[N][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 = 0 ; j <= m ; j ++)
            for(int k = 0 ; k * v[i] <= j ; k ++) 
             // 注意k可取0,便可得到 从i-1件物品中选择体积不超过j的最大价值,故无需判断
                dp[i][j] = max(dp[i][j] , dp[i-1][j - k * v[i] ] + k * w[i]);
                
    cout << dp[n][m] << endl;
    return 0;
}

1.2.2 二维空间优化

  1. v表示v[i],w表示w[i],即v为第 i 件物品的体积,w为第 i 件物品的价值

        dp[ i , j ] = max { dp[ i - 1, j ] , dp[ i - 1 , j - v ] + w, dp[ i - 1, j - 2v ] + 2w , dp[ i - 1, j - 3 v] + 3w , … … }
        dp[ i , j - v ] = max {                dp[ i - 1, j - v ]       , dp[ i - 1 , j - 2v ] + w   , dp[ i - 1, j - 3v ] + 2w , … … }
2. 显然 dp[ i ,j ] = max { dp[ i - 1 , j ] , dp[ i , j - v ] }

#include<iostream>

const int N = 1010;

using namespace std;

int n , m;
int v[N] , w[N];
int dp[N][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 = 0 ; j <= m ; j ++)
            if(v[i] <= j)
                dp[i][j] = max(dp[i-1][j] , dp[i][j - v[i] ] +  w[i]);
            else dp[i][j] = dp[i-1][j];
                
    cout << dp[n][m] << endl;
    return 0;
}

1.2.3 一维空间优化

  1. 完全背包,体积顺序
#include<iostream>

const int N = 1010;

using namespace std;

int n , m;
int v[N] , w[N];
int dp[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 ++)
                dp[j] = max(dp[j] , dp[j - v[i] ] +  w[i]);
                
    cout << dp[m] << endl;
    return 0;
}
01背包完全背包
体积 j 的变化m -> v[ i ]v[i] -> m
状态转移dp[ j ] = max( dp[ j ] , dp[ j - v[ i ] ] + w[ i ] );dp[ j ] = max( dp[ j ] , dp[ j - v[ i ] ] + w[ i ] );
  • 显然只有体积 j 的变化

1.3 多重背包

1.3.1 二维空间

  • 与完全背包类似,只不过 k 限制条件 增加了 k <= s[ i ]
  • 完全背包的k的限制条件只有 k * v[ i ] <= j
#include<iostream>

using namespace std;

const int N = 110;

int v[N] , w[N] , s[N];
int dp[N][N];

int main(){
    int n , m;
    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 ++)
                    dp[i][j] = max(dp[i][j] , dp[i - 1][j - k * v[i]] + k * w[i]);
                
    cout << dp[n][m] << endl;
    
    return 0;
}

1.3.2 二维空间 + 二进制优化

二进制优化

  • 1024 可有 1 ,2 , 4 , 8 ,… … , 512 凑出来
  • 故 第 i 中 物品数量 s[ i ] ,可由 1 , 2 , 3 ,… … , 2 ^ (k - 1) , s[ i ] - 2 ^ k 凑出;
    2 ^ (k - 1) < s[ i ] < 2 ^ k
  • 最后展开为01背包问题
#include<iostream>

using namespace std;

const int N = 13010;

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

int main(){
    cin >> n >> m;
    
    int cnt = 0;
    for(int i = 1 ; i <= n ; i ++)
    {
        int a , b , s;
        cin >> a >> b >> s;
        
        int k = 1;
        while( k <= s){
            cnt ++;
            v[cnt] = k * a;
            w[cnt] = k * b;
            s -= k;
            k *= 2;
        }
        if( s > 0)
        {
            cnt ++;
            v[cnt] = s * a;
            w[cnt] = s * b;
        }
    }
    
    n = cnt;
    
    for(int i = 1 ; i <= n ; i ++)
        for(int j =  0 ; j <= m ; j ++)
        {
            dp[i][j] = dp[i-1][j];
            if(v[i] <= j)
                dp[i][j] = max(dp[i][j] , dp[i-1][j-v[i]] + w[i]);
        }
    
    cout << dp[n][m] << endl;    
}

1.3.3 一维空间

#include<iostream>

using namespace std;

const int N = 11010;

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

int main(){
    cin >> n >> m;
    
    int cnt = 0;
    for(int i = 1 ; i <= n ; i ++)
    {
        int a , b , s;
        cin >> a >> b >> s;
        
        int k = 1;
        while( k <= s){
            cnt ++;
            v[cnt] = k * a;
            w[cnt] = k * b;
            s -= k;
            k *= 2;
        }
        if( s > 0)
        {
            cnt ++;
            v[cnt] = s * a;
            w[cnt] = s * b;
        }
    }
    
    n = cnt;
    
    for(int i = 1 ; i <= n ; i ++)
        for(int j = m ; j >= v[i] ; j --)
            dp[j] = max(dp[j] , dp[j-v[i]] + w[i]);
    
    cout << dp[m] << endl; 
}

1.4 分组背包

  • 01背包变形
  • 每组只能取一件,每组遍历一遍即可

1.4.1 二维空间

#include<iostream>

using namespace std;

const int N = 110;

int v[N][N] , w[N][N] ,s[N];
int dp[N][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 >> v[i][j] >> w[i][j];
    }
    
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 0 ; j <= m ; j ++)
        {   
            dp[i][j] = dp[i-1][j];
            for(int k = 1 ; k <= s[i] ; k ++)
                if(v[i][k] <= j)
                    dp[i][j] = max(dp[i][j] , dp[i-1][j - v[i][k]] + w[i][k]);
        }
    cout << dp[n][m] << endl;
    return 0;
}

1.4.2 一维空间

#include<iostream>

using namespace std;

const int N = 110;

int v[N][N] , w[N][N] ,s[N];
int dp[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 >> 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 <= s[i] ; k ++)
                if(v[i][k] <= j)
                    dp[j] = max(dp[j] , dp[j - v[i][k]] + w[i][k]);
                    
    cout << dp[m] << endl;
    return 0;
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值