zoukankan      html  css  js  c++  java
  • 各种背包乱搞

    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]);
            }
        }
  • 相关阅读:
    SGU 176.Flow construction (有上下界的最大流)
    POJ 2391.Ombrophobic Bovines (最大流)
    poj 1087.A Plug for UNIX (最大流)
    poj 1273.PIG (最大流)
    POJ 2112.Optimal Milking (最大流)
    SGU 196.Matrix Multiplication
    SGU 195. New Year Bonus Grant
    关于multicycle path
    ppt做gif动图
    codeforces 598A Tricky Sum
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11192655.html
Copyright © 2011-2022 走看看