三种背包模板

零一背包倒着搜,完全背包正着搜,多重背包二进制存转化成01背包。

0-1背包

对于每种物品只有取与不取两个选择。

洛谷p1048

//https://www.luogu.com.cn/problem/P1048
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;

int cost[maxn];
int val[maxn];
int dp[105][maxn];        //dp[i][j]  当背包容积为j时,放入i的物品所获得的最大价值

int main()
{
    int t,m;
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&cost[i],&val[i]);
    }
    for(int i=0;i<m;i++)
    dp[0][i]=0;
    for(int i=1;i<=m;i++)
        for(int j=t;j>=0;j--)
        {
            if(j>=cost[i])        //背包容积可以放下当前物品
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-cost[i]]+val[i]);
            else                  //放不下
            dp[i][j]=dp[i-1][j];
        }
    printf("%d\n",dp[m][t]);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;

int cost[maxn];
int val[maxn];
int dp[1005];        //dp[j]  当背包容积为j时,所获得的最大价值

int main()
{
    int t,m;
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&cost[i],&val[i]);
    }
    for(int i=0;i<m;i++)
    dp[i]=0;
    for(int i=1;i<=m;i++)          //滚动数组,依次遍历每个物品
        for(int j=t;j>=cost[i];j--) //当j小于cost[i]时,直接沿用原来的值  与二维 else dp[i][j]=dp[i-1][j];对应
        {
            dp[j]=max(dp[j],dp[j-cost[i]]+val[i]);
        }
    printf("%d\n",dp[t]);
    return 0;
}

完全背包

每种物品可以取无限多个。(在大部分情况下,用贪心思想,选性价比最高的物品其实就是答案,大概80%)。

洛谷p2871

//https://www.luogu.com.cn/problem/P2871
//因数据范围问题,此代码无法ac,贴在这只是帮助理解
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;

int cost[maxn];
int val[maxn];
int dp[105][maxn];        //dp[i][j]  当背包容积为j时,可放入i种物品,所获得的最大价值

int main()
{
    int t,m;
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&cost[i],&val[i]);
    }
    for(int i=0;i<m;i++)
    dp[0][i]=0;
    for(int i=1;i<=m;i++)
        for(int j=0;j<=t;j++)
        { 
            if(j>=cost[i]) //背包可以再放一个i类型的物品
            dp[i][j]=max(dp[i-1][j],dp[i][j-cost[i]]+val[i]);
            else  
            dp[i][j]=dp[i-1][j];
        }
    printf("%d\n",dp[m][t]);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 10000;

int cost[maxn];
int val[maxn];
int dp[100000];        //dp[i][j]  当背包容积为j时,放入i的物品所获得的最大价值

int main()
{
    int t,m;
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&cost[i],&val[i]);
    }
    for(int i=0;i<m;i++)
    dp[0]=0;
    for(int i=1;i<=m;i++)
        for(int j=cost[i];j<=t;j++)
        {
            dp[j]=max(dp[j],dp[j-cost[i]]+val[i]); //当j小于cost[i]时,直接沿用原来的值
        }
    printf("%d\n",dp[t]);
    return 0;
}

多重背包

每种物品限定了数量。

可以把每一个都看成不同类型的物品,这样就转化成0-1背包问题了。

但是直接转换会导致复杂度飙升,可以利用2进制来优化。

以19为例子 它可以拆分\(1+2+4+8+3\),其实就是\(2^0 + 2^1 + 2^2 + 2^3 +3\)

按照上面的划分,可以把19个同类型的背包拆分成5种不同类型的背包,比一个一个拆分要好得多。

洛谷p1776

//https://www.luogu.com.cn/problem/P1776
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
const int maxn = 40005;

int dp[maxn];
int val[maxn<<2];
int wei[maxn<<2];


int main()
{
    int n,mw;
    scanf("%d%d",&n,&mw); //种类  最大容积
    int v,w,m; //价值 重量 数量
    int cnt=1;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d%d",&v,&w,&m);
        for(int j=1;j<=m;j<<=1)   //二进制优化
        {
            val[cnt]=v*j; wei[cnt]=w*j;
            m-=j; cnt++;
        }
        if(m) 
        {
            val[cnt]=v*m; wei[cnt]=w*m,cnt++;
        }
    }
    //for(int i=0;i<maxn;i++) dp[i]=0;
    for(int i=1;i<=cnt-1;i++)  //变成0-1背包问题
        for(int j=mw;j>=wei[i];j--)
        dp[j]=max(dp[j],dp[j-wei[i]]+val[i]);
    printf("%d\n",dp[mw]);
    return 0;
}
posted @ 2019-12-05 14:15  chilkings  阅读(680)  评论(0编辑  收藏  举报