Dividing(HDU 1059)(多重背包_二进制优化)

题目链接:
题意:有价值分别为1,2,3,4,5,6的marbles(大理石)若干,问是否能使这些marbles平分。【marbles总数不超过2e4(很明显这是一个大常数,所以用二进制优化来做)】

My_idea(关于二进制优化)

首先的话我们先看14=1+2+4+7({1,2,4,7}可以组成1到14之间的所有的数,不多也不少,这个很重要必须要不多也不少!如果是{1,2,4,8}那么还会有15,这就不可以!)

所以利用这一点我们可以将num[i]按照二进制划分为1,2,4,……这些数,也就是将num[i]个价值为i的物品划分为了1,2,4,……个价值为i * 1 , i * 2, i * 4……的物品,这样的话我们就可以将后者看成一个数量小的01背包再来处理。(不这样优化的话我们就是一个大常数的多重背包,会T)

其次的话,物品只有价值和数量这两个属性,对于一种物品我们二进制转换之后就只有(新)价值这一个属性,而且我们要的答案是能否将所有的marbles价值平分,所以我们的dp[i]就可以用来看价值为i的状态是不是存在,最后只需要看dp[C]是否存在即可。要注意的就是1、dp[j-val]要存在,dp[j]才能存在;2、dp[0]是背包中无价值的状态,初始化要为1。

My_feeling:

首先就题意来说读了n久没读懂,就是这句话:
The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0’’. The maximum total number of marbles will be 20000.
不知道为什么我开始的时候就是觉得是20000是前边说的那个样例的价值,我就说到底是怎么得来的?也太迷了吧??一脸懵……QAQ
后来不行了实在是不懂题意,就看他们的博客,写的题意也就是题目字面意思,输入的六个数是大理石的数量,可是20000到底什么什么鬼???于是又开始一个词一个词读,我的天呐,从前天晚上就读了的题到今天上午终于读懂了,原来是数量和的最大值?!这真的说明了我是个白痴……QAQ

读懂题意之后,开始想题,知道这个该用二进制优化,但是不知道怎么处理,于是就想问问谁,但是又找不到人问(好卑微,没啥熟人),博客吧由于种种原因看不下去,就昨天晚上到现在就一直打开这道题的界面然后心烦、心烦、心烦……就打开QQ音乐听歌……先要positive起来……QAQ

下午要打排位了,不知道吧,因为什么,就强迫自己看博客,完完整整看了一遍代码,然后终于理解了之后就开始自己敲。敲了样例过了,感觉没问题,交!WA!于是发现没有初始化dp[0],理解不够深刻吧,再交还是又WA!然后又看了眼题,发现要每个cases中间有blank line隔开,于是又调整了以下,再交!还是WA!又开始debug,发现num[i]没有判零,改了交,WA!然后又发现后边val没有判零,交了!WA!然后就各种怀疑自己的代码,改了些稀奇古怪的地方还是WA了几次,中间还CE了一次。然后就开始试边界20000,发现直接就运行结束了报错,然后就调试了0 0 0 0 0 2000样例,发现中间 j=num[i]; 这一句敲飘了,写成了num[j]直接就超数组大小了。改完交了终于AC了!QAQ……提起AC又想起跳waacking的AC,interesting!

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN=12e4;

int dp[maxN],C;
int num[10];
int val,sum;

void init()
{
    sum=0;
    memset(dp,0, sizeof(dp));
    dp[0]=1;//背包内价值为0这个状态是可以存在的
}

int main()
{
    int Case=0;
    while(true)
    {
        if(Case)
            printf("\n");
        init();
        for(int i=1;i<=6;i++)
        {
            scanf("%d",&num[i]);
            sum+=num[i]*i;
        }
        if(!sum)//输入为0 0 0 0 0 0
            break;
        if(sum&1)//sum为奇数,一定不能平分
        {
            printf("Collection #%d:\nCan't be divided.\n",++Case);
            continue;
        }
        C=sum/2;
        for(int i=1;i<=6;i++)
        {
            if(!num[i])//如果是0的话就不能再跑了
                continue;
            int j;
            for(j=1;j<=num[i];j<<=1)//将num[i]个i价值的物品转换为一个01背包,假如说是14=1+2+4+7,{1,2,4,7}可以组成1到14所有的数值,所以将价值重新定义后,就是一个01背包
            {//(二进制优化->可以将多重背包物品数量的大常数转换为小常数)
                val=i*j;
                for(int k=C;k>=val;k--)
                {
                    if(dp[k-val]&&(!dp[k]))//k状态没走过,k-val状态走过,那么此刻加上val就是可走的
                        dp[k]=1;//即dp[(k-val)+val]可以有
                }
                num[i]-=j;
            }
            j=num[i];//此时的num[j]->2的次幂之外的数
            val=i*j;
            if(val)//如果是0就不能跑
            {
                for(int k=C;k>=val;k--)
                {
                    if(dp[k-val]&&(!dp[k]))
                        dp[k]=1;//即dp[(k-val)+val]可以有
                }
            }
        }
        if(dp[C])
            printf("Collection #%d:\nCan be divided.\n",++Case);
        else
            printf("Collection #%d:\nCan't be divided.\n",++Case);
    }
    return 0;
}

因为一个空格行的输出! PE了5次又!

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN = 12e4 + 5;
const int maxE = 1e4 +5;

int a[10];
int dp[maxN];

int main()
{
    int Case = 0;
    while(~scanf("%d", &a[1]))
    {
        int sum = a[1];
        for(int i = 2; i <= 6 ; i ++ )
        {
            scanf("%d", &a[i]);
            sum += a[i] * i;
        }
        if(sum == 0)
            break;
        if(sum & 1)
        {
            printf("Collection #%d:\nCan't be divided.\n\n", ++ Case );
            continue;
        }
        int C = sum >> 1;
        memset(dp, 0, sizeof(dp)); dp[0] = 1;
        // 目的:找所有apart大理石可的价值
        for(int val = 1; val <= 6 ; val ++ ) // 价值
        {
            for(int num = 1 ; num <= a[val] ; num <<= 1)
            {
                int new_ = num * val; // 新价值
                for(int j = C ; j >= new_ ; j --)
                    if(!dp[j] && dp[j - new_])
                        dp[j] = 1;
                a[val] -= num;
            }
            if(a[val])
            {
                int new_ = a[val] * val;
                for(int j = C ; j >= new_ ; j -- )
                    if(!dp[j] && dp[j - new_])
                        dp[j] = 1;
            }
        }
        if(dp[C])
            printf("Collection #%d:\nCan be divided.\n\n", ++ Case);
        else
            printf("Collection #%d:\nCan't be divided.\n\n", ++ Case);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值