分组背包,混合背包,有依赖的背包

一.分组背包(每组只能选一个)
所谓分组背包,就是把物品分成n组,每组里面m个物品,从这n组中每组选一个物品,使得在背包体积是V的条件下价值最大;
思路:对于每一组由于只能选一个,所以就是决策这一组中选哪一个获得的价值最大;

for(int i=1;i<=n;i++)   //枚举这是第几组   ,num[i]代表第i组物品的个数,v[i][j]表示第i组第j个物品的体积,w同理
{
    for(int k=V;k>=0;k--)  //枚举体积
        for(int j=1;j<=num[i];j++)  //枚举这一组内的第几个物品
            {
                if(k>=v[i][j]) 
                    dp[k]=max(dp[k],dp[k-v[i][j]]+w[i][j])   //进行决策。
            }
}

二:混合背包
混合背包就是给n个物品,其中一些物品只有一个,还有一些有有限个,另一些有无限个,求取在背包体积为V的条件下所能达到的最大价值.
思路:把物品重新进行分类,有限个可以通过二进制优化为01背包,一个就是01背包,然后标记一下当前物品是要进行01背包还是完全背包就完事儿.

int cnt=0,w[SIZE],v[SIZE],mark[SIZE];  //cnt为分堆后物品的个数,v为花费,w为价值,mark标记数组,标记这个物品进行的种类
for(int i=1;i<=n;i++)
{
    int val,cost,s;
    cin>>val>>cost>>s;
    if(s==-1)  //-1为完全背包,标记为1
    {
        w[++cnt]=val;v[cnt]=cost;mark[cnt]=1;
    }
    else if(s==1)  //01背包
    {
        w[++cnt]=val;v[cnt]=cost;mark[cnt]=0;
    }
    else  //多重背包
    {
        int k=1;
        while(k<s)
        {
            w[++cnt]=val*k;v[cnt]=cost*k;mark[cnt]=0;
            s-=k;
            k<<=1;
        }
        if(s) {w[++cnt]=val*s;v[cnt]=cost*s;mark[cnt]=0;}
    }
}
//根据分组进行对应的01背包和完全背包 balabalabala

三.有依赖的背包
简单来说就是给n个物品,但这n个物品有些之间有依赖关系,比如a和b,如果要选a,就必须选择b,选择了b,却不一定选择a,在这种条件下体积V能达到的最大价值.
思路:曾经碰到的时候一看题解就觉得很复杂,于是暂时放了下,后来在学树形DP时,又看到这个问题,其实这个本质就是一个树形DP;因为这n个物品的关系网就构成了一棵树,在树上进行决策。我们假设已经建好了物品之间的关系树

//模板
void dfs(int now,int pre)  //dp[i][j]代表以i为根结点,最大体积为j时能获得的最大价值
{
    for(int i=head[now];~i;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre) continue;
        dfs(v,now);   //先更新子节点的状态,再更新父节点的状态
        for(int j=V-cost[now];j>=0;j--)   //为什么是V-cost[now],因为我们必须要拿当前的物品才能拿这个物品下面的物品,因此要预留一个当前物品的空间来更新状态
            for(int k=0;k<=j;k++)   //枚举子节点的每个状态
                dp[now][j]=max(dp[now][j],dp[now][j-k]+dp[v][k]);//如果我要拿子物品,那么当前就需要预留子物品所占的体积,也就是k              
    }
    //更新dp[now][];
    for(int i=V;i>=cost[now];i--) dp[now][i]=dp[now][i-cost[now]]+val[now];   //子节点对当前节点的影响都更新完了,应该更新当前节点的状态,由于于要拿字节点,所以当前节点必须拿
    for(int i=0;i<cost[now];i++) dp[now][i]=0;   //拿不了当前物品的,其子节点也不能拿,也就是最大价值是0;
}

用这个方法去写“金明的预算方案”的时候T了,真是杀鸡用了牛刀,因为附件只有0,1,2三种情况,就相当于多了几种情况的01背包,在每种情况中取最大就行了

#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<time.h>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<functional>
#include<stack>
#include<map>
#include<queue>
#include<cstring>
#define mod (1000000000+7)
#define middle (l+r)>>1
#define SIZE 10000000+10
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double lb;
const int inf_max = 0x3f3f3f3f;
const ll Linf = 9e18;
const int maxn = 100+10;
const long double nature = 2.7182818;
const double eps=0.0001;
using namespace std;
int V,n,cost[maxn],val[maxn],dp[40000],fv[maxn][5],fw[maxn][5];
// 思路:枚举每一个主件,在每个情况下更新最大值
void DP()
{
    for(int i=1;i<=n;i++)
        for(int j=V;j>=cost[i];j--)   //逆序枚举,因为只能拿一个主件
        {
            dp[j]=max(dp[j],dp[j-cost[i]]+val[i]);
            if(j>=cost[i]+fw[i][1])   //要拿附件1必须要拿主件
                dp[j]=max(dp[j],dp[j-cost[i]-fw[i][1]]+val[i]+fv[i][1]);
            if(j>=cost[i]+fw[i][2])
                dp[j]=max(dp[j],dp[j-cost[i]-fw[i][2]]+val[i]+fv[i][2]);
            if(j>=cost[i]+fw[i][1]+fw[i][2])
                dp[j]=max(dp[j],dp[j-cost[i]-fw[i][2]-fw[i][1]]+val[i]+fv[i][1]+fv[i][2]);
        }
}
void Initial()
{
    memset(dp,0,sizeof(dp));
    memset(fv,0,sizeof(fv));
    memset(fw,0,sizeof(fw));
}
int main()
{
    while(cin>>V>>n)
    {
        Initial();
        for(int i=1;i<=n;i++)
        {
            int v,p,q;
            cin>>v>>p>>q;
            if(!q)   //是主件
            {
                cost[i]=v;
                val[i]=v*p;
            }
            else
            {
                fw[q][0]++;
                fw[q][fw[q][0]]=v;
                fv[q][fw[q][0]]=v*p;
            }
        }
        DP();
        printf("%d\n",dp[V]);
    }
    return 0;
}


发布了33 篇原创文章 · 获赞 14 · 访问量 422

猜你喜欢

转载自blog.csdn.net/qq_44077455/article/details/103447030
今日推荐