完全背包问题

完全背包问题

1.1 题目描述

  • 有N中物品和一个容量为V的背包。每种物品都有无限件。第i种物品体积为w[i],价值是v[i]。要求放入背包中的物品价值总和是最大的。

1.2 基本思路

  • 这个问题和上篇讲过的01背包问题有点像,不同的是每件物品都有无限件。按照01背包的思路,每件物品就不再是取或者不取的状态,而是可以取多件。背包不是无限大,第i物品最多只能取V/w[i]件。用dp[i][j]来表示前面i件物品放入一个容量为j的背包中价值总和最大是多少。可以写出一个状态转移方程

d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 = < k ∗ w [ i ] < = V } dp[i][j] = max\{dp[i-1][j-k*w[i]] +k*v[i] |0=<k*w[i]<=V\} dp[i][j]=max{dp[i1][jkw[i]]+kv[i]0=<kw[i]<=V}

题目连接

代码

#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
    return a > b ?  a : b;
}
int dp[1005][1005];
int main()
{
    int N, V;
    int v[1005];
    int w[1005];

    memset(dp, 0, sizeof(dp));
    scanf("%d %d\n", &N, &V);
    for(int i = 1; i <= N; i++)
        scanf("%d %d", &w[i], &v[i]);
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= V; j++)
        {
            for(int k = 0; k <= j/w[i]; k++)
            {
                if(j < w[i])    dp[i][j] = dp[i-1][j];
                else
                dp[i][j] = max(dp[i][j], dp[i-1][j - k * w[i]] + k * v[i]);
            }
        }
    }
    printf("%d\n", dp[N][V]);
    return 0;
    }

1.3 时间复杂度优化

  • 上述方法的时间复杂度为O(VNΣ W/v[i]),是比较大的。将上述状态转移方程进行优化

    d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 < = k } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 1 < = k } } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − w [ i ] − k ∗ w [ i ] ] + k ∗ v [ i ] + v [ i ] ∣ 0 < = k } } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − w [ i ] − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 < = k } } + v [ i ] d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , d p [ i ] [ j − w [ i ] ] + v [ i ] } dp[i][j] = max\{dp[i-1][j-k*w[i]]+k*v[i]|0<=k\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-k*w[i]] + k * v[i] | 1<=k\}\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-w[i]-k*w[i]] + k * v[i] + v[i] | 0<=k\}\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-w[i]-k*w[i]] + k * v[i]| 0<=k\}\}+v[i]\\ dp[i][j] = max\{dp[i-1][j], dp[i][j-w[i]]+v[i]\} dp[i][j]=max{dp[i1][jkw[i]]+kv[i]0<=k}dp[i][j]=max{dp[i1][j],max{dp[i1][jkw[i]]+kv[i]1<=k}}dp[i][j]=max{dp[i1][j],max{dp[i1][jw[i]kw[i]]+kv[i]+v[i]0<=k}}dp[i][j]=max{dp[i1][j],max{dp[i1][jw[i]kw[i]]+kv[i]0<=k}}+v[i]dp[i][j]=max{dp[i1][j],dp[i][jw[i]]+v[i]}

这样时间复杂度就从三次降到了两次

代码

#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
    return a > b ?  a : b;
}
int dp[1005][1005];
int main()
{
    int N, V;
    int v[1005];
    int w[1005];

    memset(dp, 0, sizeof(dp));
    scanf("%d %d\n", &N, &V);
    for(int i = 1; i <= N; i++)
        scanf("%d %d", &w[i], &v[i]);
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= V; j++)
        {
            if(j < w[i])    dp[i][j] = dp[i-1][j];
            else            dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i]);
        }
    }
    printf("%d\n", dp[N][V]);
    return 0;
}

1.4 空间复杂度优化

dp[i][j]只依赖于dp[i-1][j]和dp[i][j-w[i]],可以用滚动数组优化空间

代码

#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
    return a > b ?  a : b;
}
int dp[2][1005];
int main()
{
    int N, V;
    int v[1005];
    int w[1005];

    memset(dp, 0, sizeof(dp));
    scanf("%d %d\n", &N, &V);
    for(int i = 1; i <= N; i++)
        scanf("%d %d", &w[i], &v[i]);
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= V; j++)
        {
            if(j < w[i])    dp[i%2][j] = dp[(i-1)%2][j];
            else            dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-w[i]] + v[i]);
        }
    }
    printf("%d\n", dp[N%2][V]);
    return 0;
}

1.5 小结

做动态规划的题目,推出状态转移方程以及其中的意思是非常重要的

01背包问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bep_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值