XDOJ.T174_分配宝藏(算法:0-1背包)

第二篇博客。我又来刷存在感了。
这是一道老题,只不过现在有一些变化,以前要求输入多组数据,现在默认只输入一组。不过这不是这题的重点。所用的算法都一样。
这一题花了我不少时间,然而我还是没有得满分。得了满分的dalao教教我吧。
不过得分不重要 (反正我XDOJ只剩3题没得满分,要是老师说我态度不认真就太扯了吧),重要的是搞懂这题所涉及的一个重要算法:0-1背包
关于这个算法网上有很多精彩的解释,我这个萌新就不乱说了。(不过为了对得起这篇博客,我尽力详细地说一下)
关于这个题目网上也有很多解释,但我觉得他们的解释不到位。害得我花了大量时间看懂他们的博客。
虽然这题用的是0-1背包算法,但这题有一个特殊的地方,物品的价值在作为价值的同时也作为物品的重量。没看懂?我马上就解释一波。
同样,以防万一,贴一下题目。

标题

分配宝藏

类别

综合

时间限制

2S

内存限制

256Kb

问题描述

两个寻宝者找到一个宝藏,里面包含n件物品,每件物品的价值分别是W[0],W[1],…W[n-1]。
SumA代表寻宝者A所获物品价值总和,SumB代表寻宝者B所获物品价值总和,
请问怎么分配才能使得两人所获物品价值总和差距最小,
即两人所获物品价值总和之差的绝对值|SumA - SumB|最小。
也就是要使得其中一人所获物品价值与所有物品总价值的二分之一之差最小
因此可以将所有物品总价值的二分之一作为背包容量
此时物品的价值在作为价值的同时也作为物品的重量,因为挑选出来的物品的总价值不能超过所有物品总价值的二分之一,即挑选出来的物品的总重量不能超过背包容量

输入说明

输入数据由两行构成:
第一行为一个正整数n,表示物品个数,其中0 第二行有n个正整数,分别代表每件物品的价值W[i],其中0

输出说明

对于每组数据,输出一个整数|SumA-SumB|,表示两人所获物品价值总和之差的最小值。

输入样例

4
1 2 3 4

输出样例

0

自从有一次把C++代码提交到XDOJ上编译错误后,我就再也没有用C++写XDOJ的题了。

#include
#include

int dp[201][20001]={
     };  //用一维数组不利于理解(准确的说是连我自己都晕了,手动滑稽*5)
int main()
{
     
    int n;
    int w[201]={
     };
    int sum=0;  //物品的总价值
    scanf("%d",&n);
    int i=0;
    for(i=1;i<=n;i++)
    {
     
        scanf("%d",w+i);
        sum+=w[i];
    }
    int bagv=sum/2; //将所有物品总价值的二分之一作为背包容量
    int j=0;
    for(i=1;i<=n;++i)
    {
     
        if(w[i]>bagv)   //如果发现有物品的价值大于背包容量
        {
     
            int dif=w[i]-(sum-w[i]);    //最佳分配方案就是把这个物品分配给一个人,其他物品分配给另一个人
            if(dif<0)dif=-dif;
            printf("%d",dif);
            return 0;
        }
        for(j=1;j<=bagv;++j)    //标准的0-1背包算法,遍历背包容量从1到bagv的情况,目的是为后来遍历做准备
        {
     
            if(j<w[i])  //如果装不下
            {
     
                dp[i][j]=dp[i-1][j];    /*这个物品经过审核,i-1变为i,
                					但没有被装入背包,用大小为j的容量装之前审核过的所有物品*/
            }
            else
            {
     
                dp[i][j]=(dp[i-1][j]>dp[i-1][j-w[i]]+w[i])?dp[i-1][j]:dp[i-1][j-w[i]]+w[i];
                //就这么一条语句,我曾在此献上我的膝盖
                /*比较
                不把这个物品装入背包并用大小为j的容量装之前审核过的物品
                (而装之前审核过的物品所能获得的最大价值在前面已经得出)
                与
                把这个物品装入背包后再用大小为j-w[i]的容量装之前审核过的物品
                (而装之前审核过的物品所能获得的最大价值在前面已经得出)
                哪一个的总价值更大
                然后把较大者赋值给dp[i][j]
                */
            }
//            printf("J:%d dp[%d][%d]:%d\n",j,i,j,dp[i][j]);	//把这一行的注释解除可以得到下文图中的结果
        }
    }
    int dif=dp[n][bagv]-(sum-dp[n][bagv]);	/*只有当审核的物品数为n,且所取的背包容量为bagv时
    										才能100%保证所挑选出的物品的总价值最大*/
    if(dif<0)dif=-dif;  //题目要求输出绝对值
    printf("%d",dif);
    return 0;
}

代码并不长,正如我计导老师所言:“你们目前所遇到的题目的代码不应超过100行,而其中的精髓只有短短的几行。”
不说废话了。思路已经说了,下面解释一下这题最核心的部分:背包算法

题目样例不利于理解,以下面的样例为例:

4
3 2 4 6

此时:
bagv==7

双重循环中:

(小括号用中括号代替,至于原因,你自己写下博客就知道了,英文中括号会起到一些神奇的效果,中文中括号很可能会让你的博客迟迟不能通过审核。某大佬说文章中链接数大于5会被列为待审核,其中图片和一些特殊字符也算作链接)
(突然发现晚上10点以后发布的文章会一直处于未审核状态直到第二天早上9点左右,躺着中枪)

i==1时,w(i)等于3
  • j等于1时,j小于w(i),dp(i)(j)=dp(i-1)(j),即dp(1)(1)=dp(0)(1)等于0
  • j等于2时,j小于w(i),dp(i)(j)=dp(i-1)(j),即dp(1)(2)=dp(0)(2)等于0
  • j等于3时,j等于w(i),因为dp(i-1)(j)即dp(0)(3)等于0,dp(i-1)(j-w(i))+w(i)等于0+w(1)等于3,而dp(i)(j)取其中的较大值,所以dp(i)(j)=dp(i-1)(j-w(i))+w(i),即dp(1)(3)=dp(0)(0)+3等于3
  • j等于4,5,6,7时,dp(i)(j)等于3
i==2时,w(i)等于2
  • j等于1时,j小于w(i),dp(i)(j)=dp(i-1)(j),即dp(2)(1)=dp(1)(1)等于0
  • j等于2时,j等于w(i),因为dp(i-1)(j)即dp(1)(2)等于0,dp(i-1)(j-w(i))+w(i)等于dp(1)(0)+w(2)等于w(2)等于2,而dp(i)(j)取其中的较大值,所以dp(i)(j)=dp(i-1)(j-w(i))+w(i),即dp(2)(2)=dp(1)(0)+2等于2
  • j等于3时,j大于w(i),dp(i)(j)=dp(i-1)(j)(因为此时dp(i-1)(j)>dp(i-1)(j-w(i))+w(i)),即dp(2)(3)=dp(1)(3)等于3
其余的依(我)此(太)类(懒)推(了)

如图所示XDOJ.T174_分配宝藏(算法:0-1背包)_第1张图片
希望下面的表格能够帮助大家理解

0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1(3,3) 0 0 0 3 3 3 3 3
2(2,2) 0 0 2 3 3 5 5 5
3(4,4) 0 0 2 3 4 5 6 7
4(6,6) 0 0 2 3 4 5 6 7

怎么看呢?

  • 1(3,3)代表已经审核的物品数和正在审核的物品数之和为1,其中正在审核的物品的价值为3,重量(或者理解为体积)为3。上文说过,此题有一个特殊的地方:物品的价值在作为价值的同时也作为物品的重量。于是,物品的价值与重量当然是相等的了。2(2,2)等依此类推。
  • 再来看表格里面:
    第一行全是0代表:没有物品可以拿,物品的总价值当然是0
    第一竖和第二竖全是0代表:背包容量为0或1什么也装不下,物品的总价值当然也是0
    坐标代表当背包容量为j时审核前i件物品可以获得的最大价值。
    例如:表格中第三行第四列的3表示:审核前两件物品且背包容量为三时可以获得的最大价值为3。

如果你仍然没有理解0-1背包算法,推荐一篇十分精彩的文章:
通俗理解0-1背包问题解法
还不能理解的话,你可以像我一样尝试写一篇关于0-1背包的博客。一边写一边研究。反正我在写这篇博客的过程中受益良多。(手动滑稽*6)

你可能感兴趣的:(#,XDOJ,“难题”集萃)