zoukankan      html  css  js  c++  java
  • 动态规划----背包问题

    0-1背包之一

    n个重量价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求价值总和的最大值。
    限制条件{n(1,100)、wi,vi(1,100)、W(1,1000)}
    枚举+dfs:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=100;
    int w[MAXN],v[MAXN];
    int n;
    int solve(int i,int j)
    {
        int re;
        if(i==n)
            re=0;
        else if(j<w[i])
            re=solve(i+1,j);
        else
            re=max(solve(i+1,j),solve(i+1,j-w[i])+v[i]);
        return re;
    }
    int main()
    {
        int i,W;
        scanf("%d%d",&n,&W);
        for(i=0;i<n;i++)
            scanf("%d%d",w[i],v[i]);
        printf("%d
    ",solve(0,W));
        return 0;
    }
    由于dfs会出现很多重复计算的情况;
    剪枝后;
    int dp[MAXN][MAXN];
    int solve(int i,int j)
    {
        int re;
        if(dp[i][j])
            re = dp[i][j];
        else if(i==n)
            re = 0;
        else if(j<w[i])
            re = solve(i+1,j);
        else
            re = max(solve(i+1,j),solve(i+1,j-w[i])+v[i]);
        return re=dp[i][j];
    }

    动态规划
    递推1:dp[i][j]表示考虑第i个物品后w用j的最优挑选方案
    dp[n][j]=0;
    当j<w[j]时,dp[i][j]=dp[i+1][j];
    其它,dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);

    int dp[MAXN][MAXN_W];
    int solve()
    {
        for(int i=n-1;i>=0;i--)
            for(int j=0;j<=w;j++)
                if(j<w[i])
                    dp[i][j]=dp[i+1][j];
                else
                    dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[j]);
        return dp[0][w];
    }

    递推2:dp[i+1][j]表示从前i个物品中选出总重量不超过j的物品时总价值的最大值
    dp[0][j]=0;
    当j<w[i]时,dp[i+1][j]=dp[i][j];
    其它情况,dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);

    int 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+1][j],dp[i+1][j-w[i]]+v[i]);
        return dp[n][w];
    }

    状态转移:“前 i 个物品中挑选总重量不超过 j 时的状态” 向 “前 i+1 个物品中选取总重不超过 j ” 和 “前 j+1 个物品中选取总重不超过 j+w[i] 时的状态” 转移。

    void solve()
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<=w;j++)
             {
                dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
                if(j+w[i]<=W)
                    dp[i+1][j+w[i]] = max(dp[i+1][j+w[i]],dp[i][j]+v[i]);
             }
        return dp[n][W];
    }

    完全背包
    有n种重量和价值分别为wi,vi的物体。从这些物品中挑选总重量不超过W的物品,求出挑选物品价值总和的最大值。
    限制条件:{ n[1,100] 、wi,vi[1,1000]、W[1,10000]) }
    dp[i+1][j]表示从前 i 种物品中挑选总重量不超过 j 时总价值的最大值。
    dp[0][j]=0;
    dp[i+1][j] = max{ dp[ i-k*w[i]]+k*v[i] | 0<=k };

    int dp[MAXN][MAXN];
    int solve()
    {
        for(int i=0;i<n;i++)
        for(int j=0;j<=W;j++)
        for(int k=0;k*w[i]<=j;k++)
        dp[i+1][j] = max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
        return dp[n][w];
    }

    上面的程序三层循环。其实在计算每一行的结果的时候都重复计算了,j=j`+w[i],在dp[i][j`] 计算dp[i][j`-k*w[i]]+k*v[i]后,会计算dp[i][j-k`*w[i]]+k`*v[i],而当k=k`-1前面的计算就是和后面的计算是一样的。也就是当k>0的时候的计算都是重复计算。
     

    dp[i+1][j] = max{ dp[i][j-k*w[i]]+k*v[i] | 0<=k }
    ​​​​​​​           = max( dp[i][j],max{ dp[i][j-k*w[i]]+k*v[i] | 1<=k )
               = max( dp[i][j],max{ dp[i][(j-w[i])-k*w[i]]+k*v[i] | 0<=k }+v[i])
               = max( dp[i][j],dp[i+1][j-w[i]]+v[i])
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=101;
    int dp[MAXN][MAXN];
    int v[MAXN],w[MAXN];
    int n,W;
    int 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] );
        return dp[n][W];
    }
    int main()
    {
        scanf("%d%d",&n,&W);
        for(int i=0;i<n;i++)
            scanf("%d%d",&w[i],&v[i]);
        printf("%d
    ",solve());
        return 0;
    }

    使用一维数组实现0-1背包和完全背包

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=101;
    int dp[MAXN];
    int w[MAXN],v[MAXN];
    int n,W;
    int solve_01()
    {
        for(int i=0;i<n;i++)
            for(int j=W;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        return dp[W];
    }
    int solve_com()
    {
        for(int i=0;i<n;i++)
            for(int j=w[i];j<=W;j++)
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        return dp[W];
    }
    int main()
    {
        scanf("%d%d",&n,&W);
        for(int i=0;i<n;i++)
            scanf("%d%d",&w[i],&v[i]);
        char op[10];
        scanf("%s",op);
        if(op[0]=='0')
            printf("%d
    ",solve_01());
        else
            printf("%d
    ",solve_com());
        return 0;
    }
        

    0-1背包之二
    有n个重量和价值分别为wi,vi的物品。从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和最大值。
    限制条件:{ n[1,n]、wi[1,1e7]、vi[1,100]、W[1,1e9] };
    因为限制条件的变化,如果方法不变的话dp数组中的j最大为1e9,内存是超级大的,因此可以试着改变DP的对象。之前的方法中,是真多重量,这里不妨针对价值计算最小的重量。
    dp[i+1][j]表示前i个物品中挑选出价值总和为j时总重量的最小值(不存在时就是一个充分大的数值INF)。
    dp[0][0]=0;
    dp[0][j]=INF;
    dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i]);
     

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=100,MAXV=100,INF=1e9+1;
    int dp[MAXN+1][MANX*MANV+1];
    int w[MAXN],v[MAXN];
    int n,W;
    int solve()
    {
        fill(dp[0],dp[0]+MAXN*MAXV+1,INF);
        dp[0][0]=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<=MAXN*MAXV;j++)
                if(j<v[i])
                    dp[i+1][j]=dp[i][j];
                else
                    dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i]);
        int re=0;
        for(int i=0;i<=MAXN*MAXV;i++)
            if(dp[n][i]<=W)
                re = i;
        return re;
    }
    int main()
    {
        scanf("%d%d",&n,&W);
        for(int i=0;i<n;i++)
            scanf("%d%d",&w[i],&v[i]);
        printf("%d
    ",solve());
        return 0;
    }

    多重背包
    有n种不同大小的数组ai,每种各mi个。判断是否可以从这些数组之中选出若干使他们的和恰好为k。
    限制条件{ n[1,100]、ai,mi[1,100000]、k[1,100000] }
    dp[i+1][j]表示前i种数加和得到j时第i种数最多能剩余多少个(不能加和得到i的情况下为-1)
    dp[i][j]>=0时,dp[i+1][j]=m[i];
    j<a[i]或者dp[i+1][j-a[i]]<=0时,dp[i+1][j]=-1;
    其它情况,dp[i+1][j]=dp[i+1][j-a[i]]-1;
     

    #include<cstdio>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int MAXN=1e5+1;
    int a[MAXN],m[MAXN];
    int dp[MAXN];
    int n,k;
    void solve()
    {
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        for(int i=0;i<n;i++)
        for(int j=0;j<=k;j++)
            if(dp[j]>=0)
                dp[j]=m[i];
            else if(j<a[i]||dp[j-a[i]]<=0)
                dp[j]=-1;
            else
                dp[j]=dp[j-a[i]]-1;
        if(dp[k]>=0)printf("Yes
    ");
        else printf("No
    ");
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=0;i<n;i++)
            scanf("%d",a[i]);
        for(int i=0;i<n;i++)
            scanf("%d",m[i]);
        solve();
        return 0;
    }
    
  • 相关阅读:
    mysql delete 不支持表别名
    查找应用编译时所找的动态库:LD_DEBUG
    ng
    linux 开机启动自动执行某用户的脚步、程序
    理解Linux系统中的load average(图文版)
    char指针与数组(转载)
    堆 栈 静态区
    linux下which、whereis、locate、find 命令的区别
    linux c动态库编译好了,不能用。有些方法报(undefined reference)错误。
    浅谈管理系统操作日志设计(附操作日志类)
  • 原文地址:https://www.cnblogs.com/ke-yi-/p/10175828.html
Copyright © 2011-2022 走看看