zoukankan      html  css  js  c++  java
  • 背包问题

     01背包问题:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1010;
    int v[N], w[N];
    int f[N][N];
    
    int main()
    {
        int n, m;
        cin>>n>>m;
        
        for(int i = 1; i <= n; i++) cin>>v[i]>>w[i];
        
        for(int i = 1; i <= n; i++)
            for(int j = 0; j <= m; j++)
            {
                f[i][j] = f[i-1][j];
                if(j >= v[i]) f[i][j] = max(f[i][j], f[i-1][j-v[i]] + w[i]);
            }
        cout<<f[n][m]<<endl;
        return 0;
    }

    一维:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1010;
    int v[N], w[N];
    int f[N];
    
    int main()
    {
        int n, m;
        cin>>n>>m;
        
        for(int i = 1; i <= n; i++) cin>>v[i]>>w[i];
        
        for(int i = 1; i <= n; i++)
            for(int j = m; j >= v[i]; j--)
            {
                f[j] = max(f[j], f[j-v[i]] + w[i]);
            }
        cout<<f[m]<<endl;
        return 0;
    }

     转化为一维滚动数组的做法:

    f[i][j]只和 i - 1的状态有关,所以可以只用第二维的状态来表示。

    如果

    j = 0; j <= m; j++

    j 这样递减的话,那么f[j-v[i])肯定就在前面的f[j]当中就已经得出来了,重复了,然后这个f[j - v[i]]表示的其实是f[i][j-v[i]]的状态对应的是f[i][j](上一次的j等于此阶段的f[j - v[i]],因为j这个维度如果还是递增的话,那么f[j] = max(f[j], f[j-v[i]] + w[i]) 等价于 f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]; 事实上正确的应该是f[i][j] = max(f[i][j], f[i-1][j-v[i]] + w[i])。所以要把j这个容量的维度循环改为递减。

    int j = m; j >= v[i]; j--

    这样f[j]不会覆盖,f[j-v[i]]就还没有被更新过,表示的也是下一个i - 1的阶段,即:f[i-1][j-v[i]]的值。

    2 2 2 2 2 
    6 6 6 4 
    8 6 6 
    8 6 
    8

    2、完全背包问题

                                         

     f[i][j] = f[i-k*v[i]] + k * w[i];

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 1010;
    int v[N], w[N];
    int f[N][N];
    int main()
    {
        int n, m;
        cin>>n>>m;
        for(int i = 1; i <= n;i++) cin>>v[i]>>w[i];
        
        for(int i = 1;i <= n;i++)
            for(int j = 0; j <= m; j++)
                for(int k = 0; k * v[i] <= j; k++)
                {
                    f[i][j] = max(f[i][j], f[i-1][j-k*v[i]] + k * w[i]);
                }
        cout<<f[n][m]<<endl;
        
        return 0;
    }

    f[i][j] = max(f[i - 1][j], f[i-1][j-v]+w, f[i-1][j-2v]+2w,f[i-1][j-3v]+3w,.......)

    f[i,j-v] = max(      f[i-1][j-v],  f[i-1][j-2v] + w, f[i-1][j-3v] + 2w,.......)

    完全背包:f[i,j] = max(f[i - 1, j], f[i][j - v[i] +w[i]]), 

    01背包:f[i, j] = max(f[i-1, j], f[i-1][j-v[i]+w[i]);

    把k的第三层循环去掉,

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 1010;
    int v[N], w[N];
    int f[N][N];
    int main()
    {
        int n, m;
        cin>>n>>m;
        for(int i = 1; i <= n;i++) cin>>v[i]>>w[i];
        
        for(int i = 1;i <= n;i++)
            for(int j = 0; j <= m; j++){
                    f[i][j] = f[i-1][j];
                    if(j >= v[i]) 
                        f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]);
            }
        cout<<f[n][m]<<endl;
        
        return 0;
    }

     同样把完全背包优化由二维优化为一维:

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 1010;
    int v[N], w[N];
    int f[N];
    int main()
    {
        int n, m;
        cin>>n>>m;
        for(int i = 1; i <= n;i++) cin>>v[i]>>w[i];
        
        for(int i = 1;i <= n;i++)
            for(int j = v[i]; j <= m; j++){
                        f[j] = max(f[j], f[j-v[i]] + w[i]);
            }
        cout<<f[m]<<endl;
        
        return 0;
    }

    这里就没有01背包的烦忧了,背包的容量m不需要递减遍历了。因为j是从小到大枚举的,然后f[j-v[j]]肯定在前面的f[j]就枚举出现过,然后第一维代表的肯定是i,这样就跟完全背包的状态计算完全一样:f[i][j] = max(f[i - 1][j], f[i][j-v[i]] + w[i]); 没有计算过的就是下一个层次的。

    多重背包:

    f[i][j] = max(f[i-1][j-v[i]*k] + k * w[i]);k =0,1,2,……,s[i]朴素版本的完全背包问题

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N = 110;
    int v[N],w[N],f[N][N],s[N];
    
    int main()
    {
        int n,m;
        cin>>n>>m;
        for(int i = 1;i <= n;i++) cin>>v[i]>>w[i]>>s[i];
        
        for(int i = 1; i <= n;i++)
            for(int j = 0; j <= m; j++)
                for(int k = 0; k <= s[i] && k * v[i] <= j; k++){
                    f[i][j] = max(f[i][j], f[i - 1][j-k*v[i]] + k * w[i]);
                }
        cout<<f[n][m]<<endl;
    }

     优化:先同上面的完全背包一样

    f[i,j] = max(f[i-1,j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,...,f[i-1, j-sv]+sw);

    f[i-1,j-v] = max(  f[i-1][j-v],  f[i-1][j-2v]+w,... f[i-1,j-sv]+(s-1)w), f[i-1, j - (s+1)v] + sw);

    不能用前面的类似完全背包的优化方式

    二进制优化方式:

     s = 200, 1, 2, 4, 8, 16, 32, 64,  + 73

    0~127  73~200 这样就可以凑出0 ~ 200

     物品数量就可以从s降到logs,把物品的个数二进制分解,就不用从0枚举到s了。而是用logs个二进制数再加一个常数c就可以把数量s完全的表示出来。

    原来的时间复杂度:N * V * S 变成对NlogS的物品做01背包 : N * V * logS = 1000 * 2000 * 12 = 2.4e7

    多重背包的优化版本:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 24000, M = 2010;
    
    int n, m;
    int v[N], w[N];
    int f[N];
    
    int main()
    {
        cin>>n>>m;
        
        int cnt = 0;
        for(int i = 1; i <= n;i++)
        {
            int a, b, s;
            cin>>a>>b>>s;
            int k = 1;
            while(k <= s)
            {
                cnt++;
                v[cnt] = a * k;
                w[cnt] = b * k;
                s -= k;
                k <<= 1;
            }
            if(s > 0)
            {
                cnt++;
                v[cnt] = a * s;
                w[cnt] = b * s;
            }
        }
        n = cnt;
        
        for(int i = 1; i <= n; i++)
            for(int j = m; j >= v[i]; j--)
            {
                f[j] = max(f[j], f[j-v[i]] + w[i]);
                
            }
        cout<<f[m]<<endl;
    }

     分组背包问题:

    有N组,每组有若干的物品,有对应的体积和价值,每组只能选其中一样的物品。

    如果第i组物品不选的话,f[i - 1, j].

    如果选了第 i 组第 k 个物品的话:f[i - 1, v[i, k]] + w[i, k].

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 110;
    
    int v[N][N], w[N][N], s[N], f[N];
    
    int main()
    {
        int n,m;
        cin>>n>>m;
        
        for(int i = 1;i <= n; i++)
        {
            cin>>s[i];
            for(int j = 0;j < s[i]; j++)
                cin>>v[i][j]>>w[i][j];
        }
        
        for(int i = 1; i <= n; i++)
            for(int j = m; j >= 0; j--)
                for(int k = 0; k < s[i]; k++)
                    if(j >= v[i][k]) f[j] = max(f[j], f[j-v[i][k]] + w[i][k]);
        cout<<f[m]<<endl;
    }
  • 相关阅读:
    Shiro认证过程?
    使用过Redis做异步队列么,你是怎么用的?
    使用 Spring Boot有什么好处
    Spring Boot、Spring MVC 和 Spring 有什么区别?
    es
    python并发编程
    Go基础05
    Go04基础
    Go03基础
    Go基础02
  • 原文地址:https://www.cnblogs.com/longxue1991/p/12741161.html
Copyright © 2011-2022 走看看