一、01背包

问题描述:

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

解答:

状态转移方程:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

即将问题分解为“将前i件物品放入容量为v的背包中”这个子问题,如果不放第i件物品,则f[i][v]=f[i-1][v],如果放这就是f[i-1][v-c[i]]+w[i],选取最大那个;

以上式子可以优化空间度为: f[v]=max{f[v],f[v-cost]+weight},循环顺序为倒序;

有的题目要求“恰好装满背包”时的最优解,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解;否则初始化为0即可;

 

二、完全背包问题

问题描述:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
解答:

状态转移方程:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}

优化成一维背包:f[v]=max{f[v],f[v-c[i]]+w[i]},循环顺序为正序

 

三、多重背包

问题描述:
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

解答:

状态转移方程:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}

代码如下:

#include<bits\stdc++.h>
using namespace std;
const int maxn=1e6;

int dp[maxn];
int value[maxn],weight[maxn],number[maxn];
int bag;

void ZeroOnePack(int weight,int value)
{
    for(int i=bag;i>=weight;i--)
    {
        dp[i]=max(dp[i],dp[i-weight]+value);
    }
}

void CompletePack(int weight,int value)
{
    for(int i=weight;i<=bag;i++)
    {
        dp[i]=max(dp[i],dp[i-weight]+value);
    }
}

void MultiplePack(int weight,int value,int number)
{
    if(bag<weight*number)
    {
        CompletePack(weight,value);
        return;
    }
    else
    {
        int k=1;
        while(k<=number)
        {
            ZeroOnePack(k*weight,k*value);
            number-=k;
            k*=2;
        }
        ZeroOnePack(number*weight,number*value);
    }
}

int main()
{
    int n;
    while(~scanf("%d%d",&bag,&n))
    {
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&number[i]);
            scanf("%d",&weight[i]);
            scanf("%d",&value[i]);
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            MultiplePack(weight[i],value[i],number[i]);
        }
        cout<<dp[bag]<<endl;
    }
}