动态规划——01背包

 

题目描述

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vi,价值是 w

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

输入格式

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

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

输出格式

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

数据范围

≤ 1000
vw≤ 1000

     

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8



动态规划————要想达到最终状态,就要利用之前的状态。

 

0 1背包——从样例剖析

 

样例对应物品编号的体积与价值表:

i   v( i ) w( i )
1 1 2
2 2 4
3 3 4
4 4 5

( i是物品编号,v ( i ) 是物品体积,w ( i ) 是物品价值,j 是背包容量)

 

f ( i , j ) 是从 1 ~ i 个物品中选并且这些物品总体积不能超过j的价值最大的选法

f ( i , j )表:

i \ j  0 1 2 3 4 5
0 0 0 0 0 0
1 2 2 2 2 2
2 0 4 6 6 6
3 2 4 6 6 8
4 0 2 4 6 6 8

 

f ( i , j ) = max ( f ( i - 1 , j ) , f ( i - 1 , j - v ( i ) ) + w ( i ) ),

① f ( i - 1 , j ) 代表不把第i个物品选上,只选择 1 ~ i - 1 个物品的选择并且总体积不超过 j ,

② f ( i - 1 , j - v ( i ) + w ( i ) 代表选上第i个物品,但不能直接在 f ( i - 1 , j ) 上加,还要保证总体积
不超过 j - v ( i )。

f ( i , j ) 在①②两种情况中选价值最大的。

例如 f ( 3 , 3 ) 选的是第 1 件和第 2 件物品 f ( 3 , 3 ) = f ( 2 , 3 ) = 6 ; f ( 3 , 5 ) 选的是第 2 件和第 3 件物品 f ( 3 , 3 ) = f ( 2 , 5 - 3 ) + 4 = 8

 

代码

#include<cstdio>
#include<cmath>
using namespace std;
const int N=1010;
int v[N],w[N];//v[1~n]代表1~n物品的体积,w[1~n]代表1~n的价值
int f[N][N];//f[0][0~n]=0,f[0~n][0]=0  (全局变量默认都为0)
int n,m;//分别代表物品种数,背包总体积
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            if(j-v[i]>=0) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
            else f[i][j]=f[i-1][j];
    }
    printf("%d",f[n][m]);
    return 0;
}

 


优化


由于 f ( i , j ) = max ( f ( i - 1 , j ) , f ( i - 1, j -v ( i ) ) + w ( i ) ) ,

f ( i , j ) 都是由 i - 1 层决定的,因此可以考虑一层一层的覆盖,使 f ( i , j ) 简化成 f ( j ) ,变成

f ( j ) = max ( f ( j ) , f ( j - v ( i ) ) + w ( i ) )

 


 

下面看循环条件还是 i 从 1 ~ n ,j 从 1 ~ m 的举例,

初始化:当 i = 0 时, f ( 0 ~ m ) = 0

0 1 2 3 4 5
0 0 0 0 0 0

 

i = 1 时,f ( 1 ~ m ) :

f ( 1 ) = max ( f ( 1 ) , f ( 1 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

f ( 2 ) = max ( f ( 2 ) , f ( 2 - 1 ) + 2 ) = max ( 0 , 2 + 2 ) = 4 ;

 

0 1 2 3 4 5
0 2 4      

 

对比二维,f ( 2 ) 应该等于 2 才对,在二维中 f ( 1 , 2 ) 比较的应该是 f ( 0 , 2 ) 和 f ( 0 , 2 - 1 )即f ( 0 , 1 ) 的结果,而这里 f ( 2 ) 右边的 f ( 1 ) 不再是上一层的结果,而是刚刚算出的 f ( 1 ) 覆盖了,属于第 i 层的结果了。

因此为了避免用上层结果时,被这层结果覆盖的情况,
j 变为从 m ~ 1 ,同时将 j > = v ( i ) 的条件提上来,变成 j 从 m ~ v ( i )

下面看循环条件还是 i 从 1 ~ n ,j 从 m ~ v ( i )的举例,

初始化:当 i = 0 时,f ( 0 ~ m ) = 0

0 1 2 3 4 5
0 0 0 0 0 0

 

i = 1 时, f ( m ~ v( 1 ) ) :

f ( 5 ) = max ( f ( 5 ) , f ( 5 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

f ( 4 ) = max ( f ( 4 ) , f ( 4 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

f ( 3 ) = max ( f ( 3 ) , f ( 3 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

f ( 2 ) = max ( f ( 2 ) , f ( 2 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

f ( 1 ) = max ( f ( 1 ) , f ( 1 - 1 ) + 2 ) = max ( 0 , 0 + 2 ) = 2 ;

更新 f ( j ) :

 

0 1 2 3 4 5
0 2 2 2 2 2

i = 2 时 , f ( m ~ v ( 2 ) ) :

f ( 5 ) = max ( f ( 5 ) , f ( 5 - 2 ) + 4 ) = max ( 2 , 2 + 4 ) = 6 ;

f ( 4 ) = max ( f ( 4 ) , f ( 4 - 2 ) + 4 ) = max ( 2 , 2 + 4 ) = 6 ;

f ( 3 ) = max ( f ( 3 ) , f ( 3 - 2 ) + 4 ) = max ( 2 , 2 + 4 ) = 6 ;

f ( 2 ) = max ( f ( 2 ) , f ( 2 - 2 ) + 4 ) = max ( 2 , 0 + 4 ) = 4 ;

更新 f ( j ) :

0 1 2 3 4 5
0 2 4 6 6 6

 

i = 3 时,f ( m ~ v ( 3 ) ) :

f ( 5 ) = max ( f ( 5 ) , f ( 5 - 3 ) + 4 ) = max ( 6 , 4 + 4) = 8 ;

f ( 4 ) = max ( f ( 4 ) , f ( 4 - 3 ) + 4 ) = max ( 6 , 2 + 4) = 6 ;

f ( 3 ) = max ( f ( 3 ) , f ( 3 - 3 ) + 4 ) = max ( 6 , 0 + 4) = 6 ;

更新 f ( j ) :

0 1 2 3 4 5
0 2 4 6 6 8

 

i = 4 时 , f ( m ~ v ( 4 ) ) :

f ( 5 ) = max ( f ( 5 ) , f ( 5 - 4 ) + 5 ) = max ( 8 , 2 + 5) = 8 ;

f ( 4 ) = max ( f ( 4 ) , f ( 4 - 4 ) + 5 ) = max ( 6 , 0 + 5) = 6 ;

更新 f ( j ) :

0 1 2 3 4 5
0 2 4 6 6 8

 

 

最后 f ( j ) 结果与二维的 f ( 4 , j ) 结果一样,说明正确。


 

优化后代码:

include<cstdio>
#include<cmath>
using namespace std;
const int N=1010;
int v[N],w[N];//v[1~n]代表1~n物品的体积,w[1~n]代表1~n的价值
int f[N];//f[0~n]=0
int n,m;//分别代表物品种数,背包总体积
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&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]);
    }
    printf("%d",f[m]);
    return 0;
}

 

 

 

 


 

 

 

开心的金明:题目描述

 

#include<iostream>
using namespace std;
const int N=250000;
int n,m,v[30],w[30],dp[N];
int main(){
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
        w[i]=v[i]*w[i];
    }
    for(int i=1;i<=n;i++)
      for(int j=m;v[i]<=j;j--)
          dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

    cout<<f[m];
    return 0;
    
}

 

 

5倍经验日:题目描述

 

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1010;
long long lose[N],win[N],use[N],dp[N];
int n,x;
int main()
{
    cin>>n>>x;
    for(int i=1;i<=n;i++)cin>>lose[i]>>win[i]>>use[i];
    for(int i=1;i<=n;i++)
    {
            for(int j=x;j>=use[i];j--)
                dp[j]=max(dp[j]+lose[i],dp[j-use[i]]+win[i]);
                
            for(int j=use[i]-1;j>=0;j--)
                dp[j]+=lose[i];
        
    }
    printf("%lld",dp[x]*5);
    return 0;
}

 

posted on 2020-03-28 15:37  wang_dahua  阅读(122)  评论(0)    收藏  举报

导航