动态规划--背包

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

  对于这类问题我们我们定义f[i][j]表示在前i个物品中选总容量为j所能得到的最大价值为多少于是我们状态转移便是这样 f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);

int f[N][M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
            f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[n][i],ans);
    printf("%d\n",ans);
}

  这样一来,我们的时间复杂度就是O(nm),空间复杂度为O(nm),但我们发现f[i]的值只与f[i-1]有关,此时我们可以用滚动数组来优化空间到O(m),为f[j]=max(f[j],[j-w[i]]+v[i]);此时我们的j就要倒序枚举,因为j只会从比它小的j那转移。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

2.完全背包问题:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。第 i 种物品的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。这个问题非常类似于 01 背包问题,所不同的是每种物品有无限个。

  这时我们就有一个思路,我们将01背包倒序的滚动数组做法正序,这样我们就起到了一个物品选多次的效果

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

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

若是我们将东西直接拆成一个一个,再跑01背包,这势必会超时,这时我们要用到2进制的思想,因为任何一个数都可以拆成由1,2,4,8..因为他们的二进制是01的

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=n;i>=1;i--)
    {
        --g[i];
        for(int b=2;b<=g[i];b<<=1)
        {
            g[i]-=b;
            w[++n]=w[i]*b;
            v[n]=v[i]*b;
        }
        if(g[i]!=0)
        {
            w[++n]=w[i]*g[i];
            v[n]=v[i]*g[i];
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

4.混合三种背包问题:如果将三种背包问题混合起来。也就是说,有的物品只可以取一次(01 背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

01 背包与完全背包的混合考虑只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是 O(nm)。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        if(g[i]) for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
        else for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

如果是再加上多重背包,再加上有的物品最多可以取有限次,我们就只能拆分物品

5.二维费用的背包问题:二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。

  费用加了一维,只需状态也加一维即可。设 f[i][v][u]表示前 i 件物品付出两种代价分别为 v和 u 时可获得的最大价值。状态转移方程就是:f[i][v][u]=max(f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i])

6.分组的背包问题:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i],价值是 w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前 k 组物品花费费用 v 能取得的最大权值,则有:f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k 。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int k=1;k<=n;k++)
        for(int i=1;i<=w[k][0];i++)
            for(int j=m;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[k][i]]+v[k][i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

7.有依赖的背包问题:这种背包问题的物品间存在某种“依赖”的关系。也就是说,i 依赖于 j,表示若选物品 i,则必须选物品 j。为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品

考虑到所有这些策略都是互斥的(也就是说,你只能选择一种策略),所以一个主件和它的附件集合,实际上对应于分组的背包问题中的一个物品组,每个选择了主件又选择了若干个附件的策略对应于这个物品组中的一个物品,其费用和价值都是这个策略中的物品的值的和。但仅仅是这一步转化并不能给出一个好的算法,因为物品组中的物品还是像原问题的策略一样多。

较一般的问题更一般的问题是:依赖关系以图论中“森林”的形式给出(森林即多叉树的集合),也就是说,主件的附件仍然可以具有自己的附件集合,限制只是每个物品最多只依赖于一个物品(只有一个主件)且不出现循环依赖。解决这个问题仍然可以用将每个主件及其附件集合转化为物品组的方式。唯一不同的是,由于附件可能还有附件,就不能将每个附件都看作一个一般的01背包中的物品了。若这个附件也有附件集合,则它必定要被先转化为物品组,然后用分组的背包问题解出主件及其附件集合所对应的附件组中各个费用的附件所对应的价值。事实上,这是一种树形 DP,其特点是每个父节点都需要对它的各个儿子的属性进行一次 DP 以求得自己的相关属性。这已经触及到了“泛化物品”的思想。

8.泛化物品:考虑这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化。这就是泛化物品的概念。详细的来说的话,就是对于每一个物品,我们都一个关于重量和其价值的函数。于是我们每次合并两个泛化的物品,直至只剩一个

int f[N<<1][M],cnt=n;
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=m;k>=w[i];k--)
                f[cnt+1][j]=max(f[cnt+1][j],f[cnt][j-w[k]]+v[i][k]);
    for(int i=1;i<=m;i++) ans=max(f[n+n-1][i],ans);
    printf("%d\n",ans);
}

总代码如下:

#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#define lson l,mid,o<<1
#define rson mid+1,r,o<<1|1
using namespace std;
typedef long long ll;
inline int read()
{
    int a=0,p=0;char ch=getchar();
    while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) p=1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) a=(a<<3)+(a<<1)+ch-‘0‘,ch=getchar();
    return p?-a:a;
}
const int N=100,M=1000;
int v[N],w[N],g[N],ans=0,n,m;
namespace _01_bei_bao1
{
    int f[N][M];
    void work()
    {
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
            for(int j=w[i];j<=m;j++)
                f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
        for(int i=1;i<=m;i++) ans=max(f[n][i],ans);
        printf("%d\n",ans);
    }
}
namespace _01_bei_bao2
{
    int f[M];
    void work()
    {
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
            for(int j=m;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[i]]+v[i]);
        for(int i=1;i<=m;i++) ans=max(f[i],ans);
        printf("%d\n",ans);
    }
}
namespace _wq_bei_bao
{
    int f[M];
    void work()
    {
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
            for(int j=w[i];j<=m;j++)
                f[j]=max(f[j],f[j-w[i]]+v[i]);
        for(int i=1;i<=m;i++) ans=max(f[i],ans);
        printf("%d\n",ans);
    }
}
namespace _dc_bei_bao
{
    int f[M];
    void work()
    {
        memset(f,0,sizeof(f));
        for(int i=n;i>=1;i--)
        {
            --g[i];
            for(int b=2;b<=g[i];b<<=1)
            {
                g[i]-=b;
                w[++n]=w[i]*b;
                v[n]=v[i]*b;
            }
            if(g[i]!=0)
            {
                w[++n]=w[i]*g[i];
                v[n]=v[i]*g[i];
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=m;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[i]]+v[i]);
        for(int i=1;i<=m;i++) ans=max(f[i],ans);
        printf("%d\n",ans);
    }
}
namespace _hh_bei_bao1
{
int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        if(g[i]) for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
        else for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}
}
namespace _fz_bei_bao
{
    int f[M];
    void work()
    {
        memset(f,0,sizeof(f));
        for(int k=1;k<=n;k++)
            for(int i=1;i<=w[k][0];i++)
                for(int j=m;j>=w[i];j--)
                    f[j]=max(f[j],f[j-w[k][i]]+v[k][i]);
        for(int i=1;i<=m;i++) ans=max(f[i],ans);
        printf("%d\n",ans);
    }
}
namespace _fh_bei_bao
{
    int f[N<<1][M],cnt=n;
    void work()
    {
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++)
                for(int k=m;k>=w[i];k--)
                    f[cnt+1][j]=max(f[cnt+1][j],f[cnt][j-w[k]]+v[i][k]);
        for(int i=1;i<=m;i++) ans=max(f[n+n-1][i],ans);
        printf("%d\n",ans);
    }
}
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;i++) v[i]=read(),w[i]=read(),g[i]=read();
    // _01_bei_bao1::work();
    // _01_bei_bao2::work();
    // _wq_bei_bao::work();
    // _dc_bei_bao::work();
    // _hh_bei_bao1::work();
    // _fz_bei_bao::work();
    // _fh_bei_bao::work();
    return 0;
}
/*

*/

原文地址:https://www.cnblogs.com/cold-cold/p/9991315.html

时间: 2024-10-10 06:35:18

动态规划--背包的相关文章

Leetcode 494 Target Sum 动态规划 背包+滚动数据

这是一道水题,作为没有货的水货楼主如是说. 题意:已知一个数组nums {a1,a2,a3,.....,an}(其中0<ai <=1000(1<=k<=n, n<=20))和一个数S c1a1c2a2c3a3......cnan = S, 其中ci(1<=i<=n)可以在加号和减号之中任选. 求有多少种{c1,c2,c3,...,cn}的排列能使上述等式成立. 例如: 输入:nums is [1, 1, 1, 1, 1], S is 3. 输出 : 5符合要求5种

动态规划——背包、LIS、LCS

问题 A: 导弹拦截 时间限制: 1 Sec  内存限制: 128 MB 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意 的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所 有的导弹.输入导弹一次飞来的高度(雷达给出的高度不大于30000的正整数).计算这套系统最多能拦截多少导弹. 输入 n颗依次飞来的导弹高度,导弹颗数<=1

动态规划——背包

Wikioi 3729 飞扬的小鸟 题目描述 Description 输入描述 Input Description 输出描述 Output Description 输出文件名为 bird.out. 共两行. 第一行,包含一个整数,如果可以成功完成游戏,则输出 1,否则输出 0. 第二行,包含一个整数,如果第一行为 1,则输出成功完成游戏需要最少点击屏幕数, 否则,输出小鸟最多可以通过多少个管道缝隙. 样例输入 Sample Input 样例输出 Sample Output [输入输出样例说明]

【洛谷】【动态规划/背包】P1833 樱花

[题目描述:] 爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci.爱与愁大神在每天上学前都会来赏花.爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树可以看无数遍.但是看每棵樱花树都有一定的时间Ti.爱与愁大神离去上学的时间只剩下一小会儿了.求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学. [输入格式:] 共n+1行: 第1行:三个数:现在时间Ts(几点:几分),去上学的时间Te(几点:几分),爱与愁大神院子里有几棵樱花树n.

HDU 1284 钱币兑换问题 (动态规划 背包方案数)

钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 15134    Accepted Submission(s): 9117 Problem Description 在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法.请你编程序计算出共有多少种兑法. Input 每行只有一个正整数N,N小于32768. Outp

金明的预算方案 (背包DP)

累~~~ 题目描述 Description 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子: <dl><dd> <colgroup><col width="66"/> <col

POJ 3211 Washing Clothes 背包题解

本题是背包问题,但是需要转化成背包的. 因为是两个人洗衣服,那么就是说一个人只需要洗一半就可以了,因为不能两个人同时洗一件衣服,所以就成了01背包问题了. 思路: 1 计算洗完同一颜色的衣服需要的总时间totTime 2 利用动态规划背包法求这些衣服能在那些时间点完成 3 求比(totTime+1)/2大的最小时间点 4 得到洗一种颜色衣服的时间,那么继续求下洗一种颜色衣服的时间 5 最后加起来就是答案了. 这个是算法问题. 剩下来就是考编程功力了,因为给出的数据,需要我们自己把衣服分类,分类之

codevs——3111 CYD啃骨头(背包)

裸的01背包 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description CYD吃饭时有N个骨头可以啃,但CYD要午睡了,所以他只有M分钟吃饭,已知啃每个骨头需花费时间Ai,可以得到Bi个单位的营养.问CYD最多得到多少营养. 输入描述 Input Description M  N A1 B1 A2 B2 …… AN BN 输出描述 Output Description 得到的最大营养值 样例输入 Sample Input 10  3

codevs——2841 愤怒的LJF(背包)

样例有误! 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description LJF发现ZPC的积分比他高,他很愤怒. 他挤出T时间做题,他有Q的智慧,他只会做难度系数比他的智慧低或相等的题目. 有N道题,第i道题的时间为Ti,难度系数为Qi,可获积分Wi. LJF有M积分,ZPC有S积分,求LJF最多积分的情况下是否能超过ZPC. 输入描述 Input Description 第一行:N M T Q S 第二行到第N+1行:Ti Qi Wi