【更新】01背包问题 HDU 2602 新生赛B题题解

文章目录

    • 个人分析:
    • 个人感受:
    • 二维数组解法:
    • 0-1背包路径打印选讲:
    • 二维降为一维:
    • 新生赛题解

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

个人分析:

如何理解代码呢?我们设dp[ i ][ j ]为前i件物品放在容量为j的背包里所能得到的最大价值,那么思考一下,每个物品都只有两种可能,放或不放,这就是为什么叫做01背包的原因。那么,我们将第i件物品放在容量为j的背包中,使dp[ i ][ j ]最大,那么对于第i件物品,也只有两种操作。于是,我们可以很容易想到,我们不放第i件物品时,是不是需要先知道dp[ i - 1 ][ j ]的值呢?这就形成了dp,形成了一种递推关系。另一种可能,假设我们放第i件物品,那么,首先需要考虑当前容量j是否放得下物品i,假设放得下,将j减去w[ i ],即在剩余的体积放前i件所得到的最大的价值为dp[ i ][ j-w[ i ],所以总价值为dp[ i ][ j - w [ i ] ] +v[ i ]。
动态规划的思想:

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

个人感受:

关于01背包呢,我了解的是属于dp的,并且常考,但是思路清晰了,看这种类型题就像套公式一样的~ 代码后面附上了对01背包的理解,这是学校里一位厉害的学长总结的,我学习了他的代码~挺不错的! 然后也学了一下二维数组转变一维数组
具体代码如下:
AC

二维数组解法:

#include
#include
#include
using namespace std;
const int manx=1000+8;
int v[manx],w[manx],dp[manx][manx];
int main()
{
     
    int t;
    cin>>t;
    while(t--)
    {
     
        int n,c;
        cin>>n>>c;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            cin>>v[i];
        for(int i=1;i<=n;i++)
            cin>>w[i];
        for(int i=1;i<=n;i++)
        {
     
            for(int j=0;j<=c;j++)
            {
     
                if(j>=w[i])
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        cout<<dp[n][c]<<endl;
    }
    return 0;
}



0-1背包路径打印选讲:

HNUCM-1465: 0-1背包问题构造最优解

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000+8;
int a[maxn];
int dp[maxn][maxn];
int s[maxn][maxn];
int f[maxn];
int n,c;
int v[maxn];
int w[maxn];
int x[maxn];

void tb(int cc)
{
     
    for(int i=n;i>1;i--)
    {
     
        if(dp[i-1][cc]==dp[i][cc])
            x[i]=0;
        else
        {
     
            x[i]=1;
            cc-=w[i];
        }
    }
    x[1]=dp[n][cc]>0? 1:0;
}
int main()
{
     
    while(cin>>n>>c)
    {
     
        for(int i=1;i<=n;i++)
            cin>>v[i];
        for(int i=1;i<=n;i++)
            cin>>w[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
     
            for(int j=0;j<=c;j++)
            {
     
                if(j>=w[i])
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        cout<<dp[n][c]<<endl;
        tb(c);
        /*for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=c;j++)
            {
                cout<
        for(int i=1;i<=n;i++)
            cout<<x[i];
        cout<<endl;
    }
    return 0;
}

二维降为一维:

解释:(这里我也参考了一下别人的博文,自己第一次接触,不太好解释)
如何理解二维降一维呢?对于外层循环中的每一个i值,其实都是不需要记录的,在第i次循环时,所有的dp[ 0…v ]都还未更新时,dp[ j ]还记录着前i-1个物品在容量为j时的最大价值,这样就相当于还记录着dp[ i - 1 ][ j ]和dp[ i - 1 ][ j - w[ i ] ]+v[ i ]。

为什么要从v开始递减遍历?我举个例子,假设一个物品GG价值1000,体积为2,那么假设我们按【0……v】这个顺序遍历,那么在j=2时,dp[2] = max(dp[2], dp[0]+1000),那么dp[2] = 1000,当j=4时,dp[4]=max(dp[4], dp[2]+1000), dp[4] = 2000,这时我们再思考一下,GG将被放进背包两次!!,如果我们逆序遍历,就可以避免这种结果。

此外,这里可以进行一个常数优化,将j>=w[i]写进for循环中。

#include
#include
#include
using namespace std;
const int manx=1000+8;
int v[manx],w[manx],dp[manx];
int main()
{
     
    int t;
    cin>>t;
    while(t--)
    {
     
        int n,c;
        cin>>n>>c;
        for(int i=1;i<=n;i++)
            cin>>v[i];
        for(int i=1;i<=n;i++)
            cin>>w[i];
       memset(dp,0,sizeof(dp));
       for(int i=1;i<=n;i++)
       {
     
           for(int j=c;j>=w[i];j--)
               dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
       }
        cout<<dp[c]<<endl;
    }
    return 0;
}

【更新】01背包问题 HDU 2602 新生赛B题题解_第1张图片
【更新】01背包问题 HDU 2602 新生赛B题题解_第2张图片

【更新】01背包问题 HDU 2602 新生赛B题题解_第3张图片
【更新】01背包问题 HDU 2602 新生赛B题题解_第4张图片

新生赛题解

题目链接:1540: XP的午餐

这题居然是多组输入。。。测试的时候wa了一发,不过这题巧妙换了一下 按照思路求的是背包重量的最大值 不是原来求获得最大价值那种 因此,我们只需要交换一下模板数组即可

#include
#include
#include
#include
using namespace std;
const int maxn=1000+5;
int v[maxn],w[maxn];
int dp[maxn][maxn];
int n,m;
int main(){
     
    while(~scanf("%d %d",&m,&n)){
     
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            scanf("%d %d",&v[i],&w[i]);
        for(int i=1;i<=n;i++){
     
            for(int j=0;j<=m;j++){
     
                if(j>=v[i])
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}
学如逆水行舟,不进则退

你可能感兴趣的:(算法,动态规划,算法,c++)