dp训练第22题 luoguP1064 金明的预算方案 依赖背包

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/79969121

给定背包容量v(32000),物品个数n(60),每个物品有三个参数volume[i],(value[i]/volume[i]),fa[i],其中fa[i]为0时表示该物品是主件,不为0时表示是附件且fa[i]就是它的主件编号.
其中,每个物品最多被两件物品所依赖.

这是一个简化后的问题,只要对每个主件的附件集合求一个01背包,复杂度O(V^2*n)
但是注意到v有32000,而且每个主件只会被最多两个物品依赖,每个主件集合最多只会有五种状态(相当于四个物品).依次枚举更好,复杂度O(n*V)

代码

wa一次,因为第一次写的时候没有分组.
注意不管是泛化物品还是二进制枚举依赖物品,一定是分成互斥物品组的.
遍历顺序是 先遍历组-再遍历体积-再遍历组中物品.

关于二进制枚举,我使用了掩码mask的方式,套路如下:

for(int mask=0;mask<(1<<px);mask++)
{
    for(int pm=0;pm<px;pm++)
        if(mask&(1<<pm))
            //add it
    //do something
}

其中px是要枚举的基数个数,多做几道状压dp就熟练了.

/* LittleFall : Hello! */
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read();
inline void write(int x);
const int N = 128,V=32768;
struct Item
{
    int fa;
    int volume;
    int value;
    vector<pair<int,int>> sons;
    bool operator<(const Item &b) const
    {
        return fa<b.fa;
    }
}save[N];
int dp[V];
int main(void)
{
    #ifdef _LITTLEFALL_
    freopen("in.txt","r",stdin);
    #endif
    //std::cin.sync_with_stdio(false); 

    int v=read(),xn=read(),n=0;
    for(int i=1;i<=xn;i++)
    {
        save[i].volume=read();
        save[i].value=save[i].volume*read();
        save[i].fa=read();
        if(!save[i].fa) n++;
        else 
            save[save[i].fa].sons.
                emplace_back(save[i].volume,save[i].value);
    }
    sort(save+1,save+xn+1);

    for(int i=1;i<=n;i++)
    {
        int px=save[i].sons.size();
        for(int j=v;j>=0;j--)
            for(int mask=0;mask<(1<<px);mask++)
            {
                int volu=save[i].volume,valu=save[i].value;
                for(int pm=0;pm<px;pm++)
                    if(mask&(1<<pm))
                        volu+=save[i].sons[pm].first,valu+=save[i].sons[pm].second;     
                if(volu<=j)
                    dp[j]=max(dp[j],dp[j-volu]+valu);
            }
    }
    printf("%d\n",dp[v] );

    return 0;
}


inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

4月17日加:
使用真正的依赖背包法做一下:

/* LittleFall : Hello! */
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read();
inline void write(int x);
const int N = 64, V = 32768;
int dp[V],tmp[V];
struct Item
{
    int volume;
    int value;
    int fa;
    vector<pair<int,int>> sons;
    bool operator<(const Item & b) const
    {
        return fa<b.fa;
    }
}save[N];

int main(void)
{
    #ifdef _LITTLEFALL_
    freopen("in.txt","r",stdin);
    #endif
    //std::cin.sync_with_stdio(false); 

    int v=read(),xn=read(),n=xn;
    for(int i=1;i<=xn;i++)
    {
        save[i].volume=read();
        save[i].value=save[i].volume*read();
        save[i].fa=read();
        if(save[i].fa)
        {
            n--;
            save[save[i].fa].sons.push_back(
                {save[i].volume,save[i].value});
        }
    }
    sort(save+1,save+xn+1);

    for(int i=1;i<=n;i++)
    {
        memcpy(tmp,dp,sizeof(dp));
        for(int k=save[i].sons.size()-1;k>=0;k--)
        {
            int volu=save[i].sons[k].first,valu=save[i].sons[k].second;
            for(int j=v;j>=volu;j--)
                tmp[j]=max(tmp[j],tmp[j-volu]+valu);
        }
        for(int j=v;j>=save[i].volume;j--)
            dp[j]=max(dp[j],tmp[j-save[i].volume]+save[i].value);
    }
    printf("%d\n",dp[v] );

    return 0;
}


inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

速度约比上一版快了3倍,而且没有物品数限制.

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页