• 基础背包---01;完全;01背包之2;多重(二进制优化);二维费用流;分组


    1.01背包:

    选或不选这件

    记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值

    dp[i+1][j]=dp[i][j](j<w[i]时)

    dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])(其他)

    int dp[MAX_N+1][MAX_M+1];
    int v[i],w[i];
    int W//总重量不超过W
    void solve(){
        for(int i=0;i<n;i++){
            for(int j=0;j<=W;j++){
                if(j<w[i])dp[i+1][j]=dp[i][j];
                else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
            }
        }
        printf("%d
    ",dp[n][W]);
    }
    View Code

    2.完全背包:

    一件东西有无限件,可以取无限次

    记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值

    如果和01背包类似的话,就有三重循环,复杂度过高

    但是发现在dp[i+1][j]的计算中选择k(k>=1)个的情况,和在dp[i+1][j-w[i]]的计算中选择k-1个的情况时一样的

    所以:

    dp[i+1][j]=dp[i][j](j<w[i]时)

    dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])(其他)

    int dp[MAX_N+1][MAX_M+1];
    int v[i],w[i];
    int W//总重量不超过W
    void solve(){
        for(int i=0;i<n;i++){
            for(int j=0;j<=W;j++){
                if(j<w[i])dp[i+1][j]=dp[i][j];
                else dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
            }
        }
        printf("%d
    ",dp[n][W]);
    }
    View Code
    01int n,i,j,m,w[35],v[35],dp[205];// 最多30个物品,最大容量m=200
          cin>>m>>n;
          for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 
          for(i=1;i<=n;i++)
                for(j=m;j>=w[i];j--)//逆序 
                       dp[j]=max(dp[j-w[i]]+v[i],dp[j]);
          cout<<dp[m]<<endl;
    完全:
     int n,m,i,j,w[32],c[32],dp[202]; 
          scanf("%d%d",&m,&n);//容量,种数 
          for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 
          for(i=1;i<=n;i++)
                    for(j=w[i];j<=m;j++)
                        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
          printf("max=%d",dp[m]);
    一维的01和完全背包

    3.01背包之2

    背景和01背包一样,都是计算不超过W重量的最大价值

    但是条件变了:

    1<=n<=100

    1<=wi<=10^7

    1<=vi<=100

    1<=W<=10^9

    之前01背包的复杂度是O(nW),所以之前的方法肯定不行

    现在W太大,反而vi小了,所以从vi入手

    记dp[i+1][j]为从0到i这i+1个物品中挑选 价值为 j 时,总重量的最小值(不存在就是INF)

    所以dp[0][0]=0,dp[0][j]=INF

    dp[i+1][j]=dp[i][j](j<v[i]时)

    dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i])(其他)

    int dp[MAX_N+1][MAX_N*MAX_V+1];
    int v[i],w[i];
    int W;//最大重量
    void solve(){
        for(int i=0;i<n;i++){
            for(int j=0;j<=MAX_N*MAX_V;j++){
                if(j<v[i])dp[i+1][j]=dp[i][j];
                else dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
            }
        }
        inr res=0;
        for(int i=0;i<=MAX_N*MAX_V;i++)if(dp[n][i]<=W)res=i;
        printf("%d
    ",res);
    }
    View Code

    技巧:如果要求恰好装满背包,那么初始化dp[0][0]=0;dp[0][j]=-INF

    若没有要求必须装满,只是希望价格尽量大,初始化就应全设为0;

    4.多重背包:

    多重背包问题的思路跟完全背包的思路非常类似,只是k的取值是有限制的,因为每件物品的数量是有限制的,状态转移方程为:

        dp[i+1][j] = max{dp[i][j - k * w[i]] + v[i] | 0 <=k <= n[i]}  

    当W<=n[i]*w[i]时,可以无限取(总重不超过W)同完全背包

    当W>n[i]*w[i]时,同01背包;将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1;使得选择该物品的最佳个数都可由这些系数相加得来

    例题:tzoj5736

    #include<bits/stdc++.h>
    using namespace std;
    int m[2005],w[2005],s[2005];//个数 重量 价值
    int n,W,dp[5005];
    void zeroone(int w,int v)
    {
        int i;
        for(i=W; i>=w; i--)
            dp[i]=max(dp[i],dp[i-w]+v);
    }
    void com(int w,int v)//完全
    {
        for(int i=w;i<=W;i++)dp[i]=max(dp[i],dp[i-w]+v);
    }
    void multidp(int w,int v,int cnt)
    {
        if(cnt*w>=W)
        {
            com(w,v);
            return;
        }
        int k=1;
        while(k<=cnt)//二进制优化
        {
            zeroone(k*w,k*v);
            cnt-=k;
            k*=2;
        }
        zeroone(cnt*w,cnt*v);
        return ;
    }
    int main()
    {
        int i,j,k;
        scanf("%d%d",&n,&W);//物品数,最大体重
        for(i=0;i<n;i++)scanf("%d%d%d",&m[i],&w[i],&s[i]);
        for(i=0;i<n;i++)multidp(w[i],s[i],m[i]);
        printf("%d
    ",dp[W]);
    }
    
    5736
    5736

    5.二维费用流背包:

    在一维基础上再加一维,其他和上面的背包一样

    例题:(toj 5742)(01背包加一维)

    #include<bits/stdc++.h>
    using namespace std;
    #define INF 0x3f3f3f3f
    int dp[1005][25][85];
    int n,m,k,a[1005],b[1005],w[1005];
    int main()
    {
        int i,j,p;
        scanf("%d%d",&m,&n);
        scanf("%d",&k);
        for(i=0;i<k;i++)scanf("%d%d%d",&a[i],&b[i],&w[i]);
        for(i=0;i<=m;i++){
            for(j=0;j<=n;j++)dp[0][i][j]=INF;
        }
        dp[0][0][0]=0;
        for(i=0;i<k;i++){
            for(j=0;j<=m;j++){
                for(p=0;p<=n;p++){
                    int x=j-a[i],y=p-b[i];
                    int rec;//记录选这件 要付出的重量
                    if(x<0&&y>=0)rec=dp[i][0][y]+w[i];
                    else if(x>=0&&y<0)rec=dp[i][x][0]+w[i];
                    else if(x<0&&y<0)rec=dp[i][0][0]+w[i];
                    else if(x>=0&&y>=0)rec=dp[i][x][y]+w[i];
                    dp[i+1][j][p]=min(dp[i][j][p],rec);
                }
            }
        }
        printf("%d
    ",dp[k][m][n]);
     }
    5742

    6.分组背包:

    最外层循环每组;中间和01背包一样循环W,最内层循环每个组的物品--保证每组只选一个或不选

    例题:tzoj:5743

    #include<bits/stdc++.h>
    using namespace std;
    int n,V,t,w[31],c[31],dp[202];
    vector<int>v[12];
    int main()
    {
        int i,j,k;scanf("%d%d%d",&V,&n,&t);
        for(i=0;i<n;i++){
            int p;
            scanf("%d%d%d",&w[i],&c[i],&p);
            v[p].push_back(i);
        }
        for(i=1;i<=t;i++){
            for(j=V;j>=0;j--){
                for(k=0;k<v[i].size();k++){
                    int u=v[i][k];
                    if(j>=w[u])dp[j]=max(dp[j],dp[j-w[u]]+c[u]);//注意if判断
                }
            }
        }
        printf("%d
    ",dp[V]);
    }
    /*
    10 1 3
    2 1 1
    */
    View Code
  • 相关阅读:
    通过抓取pintpoint2的页面信息把数据存入数据库python3
    jenkins 2.282+Publish over ssh 1.22版本发布日志不能实时显示
    ELKF搭建
    pinpoint2.0.2 定制开发--增加钉钉群通知、@到具体个人,解决手机号码无法输入BUG、删除客户端需要认证、查看数据日期范围扩大等。
    通过命令行通知RANCHER重新部署程序。
    Jenkins rancher cli 配置
    centos7 outlook mailx配置
    创业公司使用的敏捷GIT FLOW管理方式(多BUG、多项目多环境,多任务并发开发,支持需求多变)
    Dubbo Admin 2.6.2 版本连接zk 集群的连接字符串配置
    jenkins典型配置-多分支选择
  • 原文地址:https://www.cnblogs.com/ydw--/p/10914952.html
Copyright © 2020-2023  润新知