HDU_2602 Bone Collector(DP)

Bone Collector

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 33651    Accepted Submission(s): 13843


Problem Description
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

 

Input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
 

Output
One integer per line representing the maximum of the total value (this number will be less than 2 31).
 

Sample Input
  
  
1 5 10 1 2 3 4 5 5 4 3 2 1
 

Sample Output
  
  
14
 
题意:01背包问题。看网上有说贪心和DP其实是都能写的。从定义出发,动态规划是指从当前所存在的所有最优状态来推出下一个最优状态。而贪心是从上一个最优状态得到下一个最优状态。贪心以后再去掌握,这里先整理一下今天掌握的DP算法。
DP算法:
首先我们应该明白我们DP的对象应该是体积从0~V每个体积v的最优状态。从0~n-1每次尝试更新各个体积下的最优状态,即当前骨头是否放入。最终得到所有骨头都考虑过后体积V的最优状态。Dp的思想由记忆化搜索引出,递归实现的方法就不再介绍,主要介绍利用递推公式引出的双层for循环实现。DP实现可以分为一维与二维数组实现。
先整理比较简单的二维数组实现: 设置dp[i][j],而对dp[i][j]的定义不同我们也可以写出不同的双层for循环来实现:

1.若dp[i][j]表示从第i个骨头开始挑选总体积小于j时,总价值的最大值。代码如下:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#define MAX_N 1010
#define MAX_V 1010

using namespace std;

int T,N,V;
int value[MAX_N];
int v[MAX_N];
int dp[MAX_N][MAX_V];
int main()
{
    cin>>T;
    while( T-- )
    {
        cin>>N>>V;
        memset(dp,0,sizeof(dp));
        for( int i = 0; i < N; i++ )
            scanf("%d",&v[i]);
        for( int i = 0; i < N; i++ )
            scanf("%d",&value[i]);
        for( int i = N-1; i >= 0; i-- )
        {
            for( int j = 0; j <= V; j++ )
            {
                if( j < value[i] )
                    dp[i][j]=dp[i+1][j];
                else
                    dp[i][j]=max(dp[i+1][j],dp[i+1][j-value[i]]+v[i]);
            }
        }
        printf("%d\n",dp[0][V]);
        
    }
    return 0;
}


2.若dp[i][j]表示从前i个骨头中选出总体积不超过j的骨头时,总价值的最大值。代码如下:
for( int i = 0; i < N; i++ )
{
    for( int j = 0; j <= V; j++ )
    {
        if( j < value[i] )
            dp[i+1][j]=dp[i][j];
        else
            dp[i+1][j]=max(dp[i][j],dp[i][j-value[i]]+v[i]);
    }
}
printf("%d\n",dp[N][V]);


3.若dp[i][j]表示从"前i个骨头中选出总体积不超过j时的状态"向"前i+1个骨头中选取总体积不超过j"和"前i+1个骨头中选取总体积不超过j+v[i]"时的状态的转移。代码如下:
for( int i = 0; i < N; i++ )
{
    for( int j = 0; j <= V; j++ )
    {
        dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
        if( j+value[i] <= V )
            dp[i+1][j+value[i]]=max(dp[i+1][j+value[i]],dp[i][j]+v[i]);
    }
}
printf("%d\n",dp[N][V]);


一维数组:
一维数组的解法是在二维数组的基础上的优化,考虑对于每块骨头i都是在i-1的基础上进行判断,而此时i-1对应各个体积已经是最优解,不需要保存,所以我们可以覆盖上去,仅利用一维数组去求解。但是要注意因为涉及到数组的覆盖,所以对于每个骨头i我们都必须从V到value[i]倒序遍历,因为递推方程为dp[j] = max( dp[j], dp[ j-v[i] ]+value[i] ),对于同一个i倒序时dp[j]改变之后便不会再被调用,但是正序的话dp[j]可能会在之后的计算中再次被调用,也就是说骨头会被多次放入。倒序就是为了防止多次放入。代码如下:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#define MAX_N 1010
#define MAX_V 1010

using namespace std;

int T,N,V;
int value[MAX_N];//
int v[MAX_N];//
int dp[MAX_V];//一位数组存储对应体积v最优解
int main()
{
    cin>>T;
    while( T-- )
    {
        cin>>N>>V;
        memset(dp,0,sizeof(dp));
        for( int i = 0; i < N; i++ )
            scanf("%d",&v[i]);
        for( int i = 0; i < N; i++ )
            scanf("%d",&value[i]);
        for( int i = 0; i < N; i++ )
        {
            //必须要求倒序
            for( int j = V; j >= v[i]; j-- )
            {
                dp[j] = max( dp[j], dp[j-v[i]]+value[i] );
            }
        }
        printf("%d\n",dp[V]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值