各种背包乱搞

01背包

#include<iostream>
using namespace
int f[1005];//体积小于等于i的情况下最大价值是多少 
int main()
{
    int n,m;
    cin >> n >> m; 
    for(int i=1;i<=n;i++)
    {
        int v,w;
        cin >> v >>  w;
        for(int j=m;j>=v;j--)//从大到小是因为没有重复计算 
        {                    //从小到大算的话f[i-v]这个状态之前被算过了 
            f[j]=max(f[j],f[j-v]+w);    
       }
    }
    cout<<f[m]<<endl;
    return 0;
}

完全背包

#include<iostream>
using namespace std;
int f[1005];//体积小于等于i的情况下最大价值是多少 
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    {
        int v,w;
        cin >> v >> w; 
        for(int j=v;j<=m;j++)//因为物品可以取无限次,那么从小到大 
        {                    //枚举 正好可以利用之前重复算过的 
            f[j]=max(f[j],f[j-v]+w);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

二维费用(体积,重量)

/*做法类比01背包,加上枚举重量的循环就可以了*/

    #include<iostream>
    using namespace std;
    int f[1005][1005];//体积小于等于i,重量小于等于j的情况下最大价值是多少 
    int main()
    {
        int n,m,t;
        cin >> n >> m >> t;
        for(int i=1;i<=n;i++)
        {
            int v,s,w;
            cin >> v >> s >>w; 
            for(int j=m ;j>=v;j--)
            {
                for(int k=t;k>=s;k--)
                {
                    f[j][k]=max(f[j][k],f[j-v][k-s]+w); 
                }
            }
        }
        cout<<f[m][t]<<endl;
        return 0;
     }

分组背包

    #include<iostream>
    using namespace std;
    int f[1005];//表示前i组物品体积不超过j的情况下的最大价值 
    int v[105];
    int w[105];
    int main()
    {
        int n,m;
        cin >> n>> m;
        for(int i=1;i<=n;i++)
        {
            int s;
            cin >> s;
            for(int j=1;j<=s;j++) cin>> v[j]>> w[j];
            for(int j=m;j>=0;j--)
            {
                for(int k=1;k<=s;k++)
                {
                    if(j>=v[k])
                    {
                        f[j]=max(f[j],f[j-v[k]]+w[k]);
                    }
                }
            }
         }
         cout<<f[m]<<endl;
         return 0;
     } 

多重背包

1.枚举num

for(int i=1;i<=n;i++)//枚举物品
    for(int j=V;j>=0;j--)//枚举体积
        for(int k=1;k<=num[i],k++)
            //这个枚举到num[i]更省心
            if(j-k*c[i]>=0)//判断能否装下.
                f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);

2.二进制拆分

用$1,2,4...2^n$可以表示出$2^{n+1}-1$以内所有的正整数

那么对1到$num[i]$进行拆分 得到多件大物品($1,2,4,8...$件物品的和)

之后跑01背包

for(int i=1;i<=n;i++)
{
    for(int j=1;j<=num[i];j<<=1)
    //二进制每一位枚举.
    //注意要从小到大拆分
    {
        num[i]-=j;//减去拆分出来的
        new_c[++tot]=j*c[i];//合成一个大的物品的体积
        new_w[tot]=j*w[i];//合成一个大的物品的价值
    }
    if(num[i])//判断是否会有余下的部分.
    //就好像我们某一件物品为13,显然拆成二进制为1,2,4.
    //我们余出来的部分为6,所以需要再来一份.
    {
        new_c[++tot]=num[i]*c[i];
        new_w[tot]=num[i]*w[i];
        num[i]=0;
    }
}

单调队列优化

for(int i=1;i<=n;i++)//枚举物品种类 
{
    cin>>c[i]>>w[i]>>num[i];//c,w,num分别对应 体积,价值,个数 
    if(V/c[i] <num[i]) num[i]=V/c[i];//求lim
    for(int mo=0;mo<c[i];mo++)//枚举余数 
    {
        head=tail=0;//队列初始化 
        for(int k=0;k<=(V-mo)/c[i];k++) 
        {
            int x=k;
            int y=f[k*c[i]+mo]-k*w[i];
            while(head<tail && que[head].pos<k-num)head++;//限制长度
            while(head<tail && que[tail-1].value<=y)tail--;
            que[tail].value=y,que[tail].pos=x;
            tail++;
            f[k*c[i]+mo]=que[head].value+k*w[i];
            //加上k*w[i]的原因:
            //我们的单调队列维护的是前i-1种的状态最大值.
            //因此这里加上k*w[i].
        }
    }
}

复杂度$O(n*V)$

有依赖的背包

多级附件:

建树存先根遍历preo[],子树大小size[]

设w[]为代价,v[]为价值

先用一个虚点作为超级祖先

 

转移时 对于某个节点,如果购买它,考虑它的子节点

如果不购买,跳过它的子树

for(int i=scc;i>=1;i--)
    {
        int node=preo[i];
        for(int j=m;j>=0;j--)
        {
            if(j<w[node])dp[i][j]=dp[i+siz[node]][j];
            else 
            dp[i][j]=max(dp[i+1][j-w[node]]+v[node],dp[i+siz[node]][j]);
        }
    }

 

posted @ 2019-07-16 07:14  Rorschach_XR  阅读(177)  评论(0编辑  收藏  举报
//雪花飘落效果