zoukankan      html  css  js  c++  java
  • 完全背包

    (N) 种物品和一个容量是 (V) 的背包,每种物品都有无限件可用。

    (i) 种物品的体积是 (vi),价值是 (wi)

    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
    输出最大价值。

    输入格式

    第一行两个整数,(N)(V),用空格隔开,分别表示物品种数和背包容积。

    接下来有 (N) 行,每行两个整数 (vi),(wi),用空格隔开,分别表示第 (i) 种物品的体积和价值。

    输出格式

    输出一个整数,表示最大价值。

    数据范围

    (0<N,V≤1000)

    (0<vi,wi≤1000)

    输入样例

    4 5
    1 2
    2 4
    3 4
    4 5
    

    输出样例:

    10
    

    最朴素方法(超时)

    #include<iostream>
    using namespace std;
    const int N = 1010;
    int f[N][N];
    int v[N],w[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;
    }
    

    优化思路

    我们列举一下更新次序的内部关系:

    f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w ,  f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
    f[i , j-v]= max(            f[i-1,j-v]   ,  f[i-1,j-2*v] + w , f[i-1,j-2*v]+2*w , .....)
    由上两式,可得出如下递推关系: 
                            f[i][j]=max(f[i,j-v]+w , f[i-1][j]) 
    

    有了上面的关系,那么其实k循环可以不要了,核心代码优化成这样:

    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]>=0)
            f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]); //可以重复的选择
    }
    

    对比一下,下面是01背包的核心代码

    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]>=0)
            f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]); //不重复的选则
    }
    

    两个代码其实只有一句不同(注意下标)

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

    f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);//完全背包问题,背包可以重复,所以不用 i -1

    因为和01背包代码很相像,我们很容易想到进一步优化。核心代码可以改成下面这样

    • f[j] = max(f[j],f[j-v[i]] + w[i])背包可以重复,所以从小到大选择
     for(int i = 1 ; i<=n ;i++)
        for(int j = v[i] ; j<=m ;j++)//注意了,这里的j是从小到大枚举,和01背包不一样
        {
                f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    

    比较简洁的写法

    #include<iostream>
    using namespace std;
    const int N = 1010;
    int f[N];
    int v[N],w[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;
    }
    

    原文地址

  • 相关阅读:
    美食小记
    美食小记
    移动端手势的七个事件库
    移动端手势的七个事件库
    利用PS把多张psd格式的图片转换为一张PDF格式
    利用PS把多张psd格式的图片转换为一张PDF格式
    常用的六个富文本编辑器
    常用的六个富文本编辑器
    如何获取公众号里面的歌曲
    快速幂算法
  • 原文地址:https://www.cnblogs.com/qscgy/p/14728567.html
Copyright © 2011-2022 走看看