二维费用,依赖,分组背包

二维费用背包

01背包进阶版

有N件物品和一个空间容量为C,重量容量为W的背包,第 i 件物品的空间费用为c[i] ,重量费用为 wi,价值是 vi每种物品仅有一件,可以选择放或不放,求将哪些物品装入背包可使价值总和最大

01背包:

f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])

空间降维写法

for (int i = 1; i <= n; i++)
    for (int j = V; j >= w[i]; j--)
        f[j] = max(f[j], f[j - w[i]] + v[i])s;

二维费用

f[i][j][k]=max(f[i−1][j][k],f[i−1][j−c[i]][k-w[i]]+v[i])

根据经验我们就可以写出

for(int i=1; i<=n; i++)//枚举物品
	for(int j=C; j>=c[i]; j--)//枚举两种状态
		for(int k=W; k>=w[i]; k--)
			f[j][k]=max(f[j][k],f[j-c[i]][k-w[i]]+v[i]);

其实就和从完全背包添加新的限制条件变形到多重背包一样,再01背包原来的状态中加一维以满足新的限制,就是二维费用背包,以此类推就不难应对二维费用完全背包,二位费用多重背包等组合拳

例题

洛谷 1507

让体积和承重有限的条件下多装载一些高卡路里的食物,每个食品只能使用一次

输入格式:

第一行 两个数 体积最大值(<400)和质量最大值(<400)

第二行 一个数 食品总数N(<50).

第三行-第3+N行

每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)

输出格式:

一个数 所能达到的最大卡路里(int范围内)

#include<bits/stdc++.h>
using namespace std;
int V,M,n;
int v[100],w[100],e[100];//体积,质量,所含卡路里
int f[500][500];
int main()
{
    scanf("%d%d%d",&V,&M,&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d%d",&v[i],&w[i],&e[i]);
    for(int i=1; i<=n; i++)//枚举物品
        for(int j=V; j>=v[i]; j--)
            for(int k=M; k>=w[i]; k--)//枚举两种状态
                f[j][k]=max(f[j][k],f[j-v[i]][k-w[i]]+e[i]);
    printf("%d",f[V][M]);

    return 0;
}

HDU 2159

物品总个数的限制

费用的条件可能以这样的方式给出:最多只能取一共M件物品

其实就相当于多了一种件数的费用,每个物品的件数费用均为1,可以付出的最大件数费用为M

一维背包的种种思想方法,往往可以应用于二位背包问题的求解中

分组背包

有N件物品和一个容量为C的背包,第 i 件物品的空间费用为c[i] ,价值是 vi这些物品被划分为若干组,每组中的物品互相冲突,最多选一件,求将哪些物品装入背包可使价值总和最大

问题转化为:是选择本组的一件,还是一件都不选?

前k组物品花费费用 j 能得到的最大价值:f [k] [j]

f[k][j]=max(f[k−1][j],f[k−1][j−c[i]]+v[i]∣物品i属于组k)
for (所有的组k)
    for (int j = V; j >= 0; j--)
        for (所有属于组k的i)
            f[j] = max{f[j], f[j - c[i]] + v[i]}

例题

洛谷1757

#include <bits/stdc++.h>
using namespace std;
const int M=1010,N=1010;
int n,m;
int f[M],a[N],b[N],c[101][20],cc[101],cn;
int main() 
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        int C;
        scanf("%d%d%d",&a[i],&b[i],&C);
        cn=max(cn,C);//cn记录共有几组
        cc[C]++;//cc[]记录第C组共有几件物品
        c[C][cc[C]]=i;//c[][]记录第C组第i件物品的的序号
    }
    for(int i=1;i<=cn;i++)//枚举cn个组
        for(int j=m;j>=0;j--)//枚举容量
            for(int k=1;k<=cc[i];k++)//枚举各组中物品的序号
            	if(j>=a[c[i][k]])
            		f[j]=max(f[j],f[j-a[c[i][k]]]+b[c[i][k]]);//套用状态转移方程
    printf("%d
",f[m]);
}

HDU 1712

依赖背包

非树形(只有主件,附件)

物品间存在某种“依赖”的关系,若i1依赖于i2,表示若选物品i1(附件),则必须选物品i2(主件)

主件和附件的概念,只有放了主件之后才能放与之相关联的附件

可以先对每种主件的 附件的集合 进行一次 01 背包处理,就可以先求出 对于每一种主件包括其附件的组合中,每种花费的最大价值

对于每一种主件的01背包处理:

for i:主件k的所有附件
    for j:价值(0 ~ n-主件价值)
        01背包递推

得到费用依次为0~C−w[i]所有这些值时相应的最大价值f′[0~C−w[i]]

那么这个主件及它的附件集合相当于V−c[i]+1 V-c[i]+1V−c[i]+1个物品的物品组,其中费用为c[i]+k c[i]+kc[i]+k的物品的价值为f′[k]+w[i] f'[k]+w[i]f
′/[k]+w[i]。也就是说原来指数级的策略中有很多策略都是冗余的,通过一次01背包后,将主件i ii转化为V−c[i]+1 V-c[i]+1V−c[i]+1个物品的物品组,就可以直接应用分组背包的算法解决问题了。

例题

洛谷 1064

HDU 3449

#include<cstdio>
#include<cstring>
int dp[60][100010];
int max(int a,int b){return a>b?a:b;}
int main()
{
    int n,tot,i,j,k;
    while(scanf("%d%d",&n,&tot)!=EOF)
    {
        int p,m,w,v;
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&p,&m);
            for(j=0;j<p;j++) 
                dp[i][j]=-1;
            for(j=p;j<=tot;j++) 
                dp[i][j]=dp[i-1][j-p];//继承上一层的结果,j-p是因为一定要买箱子
            for(j=1;j<=m;j++)
            {
                scanf("%d%d",&w,&v);
                for(k=tot;k>=w;k--)
                {
                    if(dp[i][k-w]!=-1)
                    dp[i][k]=max(dp[i][k],dp[i][k-w]+v);//01背包部分
                }
            }
            for(j=tot;j>=0;j--)//如果能更新上一层的状态,就更新。
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
        }
        printf("%d
",dp[n][tot]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zhxmdefj/p/11145037.html