hdu 1059 Dividing 多重背包

14 篇文章 0 订阅
12 篇文章 1 订阅

传送门http://acm.hdu.edu.cn/showproblem.php?pid=1059

  • 题意:

    • 给出6个数字a[1]~a[6], a[i]代表价值为i的硬币有多少.
    • 问能否把硬币平均分为两堆.
    • sum(a[i]) < 5e4 (目测数据不止5e4….?保险起见开了5e5/扶额)
  • 分析

    • 当 sum(a[i]) 为奇数时, 必定不可分
    • 判断体积为sum(a[i])/2的多重背包能否填满即可
    • 设定物品价值和体积相同, 那么如果存在恰好填满的情况, 那么一定是最大价值=背包容量.

多重背包按照二进制拆分为01背包
假设价值为w的物品有n个, 则按照 1, 2, 4, 8, ….., (1^log(n)) 拆分
设sum(1, (1^log(n)) = sum, 设 ret = n - sum;
则有在 (0~sum) 内的所有数p都可以用拆分数的和表示 (二进制表示)
而(sum ~ n) 之间的数可用 p+ret表示
拆分完毕, 0-1背包构建完成.

#include <stdio.h>
#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 2e5+10;
const int INF = 2e5+10;
int a[7], dp[MAX], sum, T, tmax, tmp;
int mmax(int a, int b) {
    return a > b ? a : b;
}
bool getin() {
    bool ret = false;
    for (int i = 1; i <= 6; ++i) {
        cin >> a[i];
        ret |= a[i];
    }
    memset(dp, 0, sizeof(dp));
    sum = 0;
    return ret;
}
bool judge() {
    for (int i = 1; i <= 6; ++i)
        sum += (i*a[i]);
    if (sum & 1)
        return false;
    sum >>= 1;
    for (int i = 1; i <= 6; ++i) {
        if (a[i] == 0) continue;
        int k;
        for (k = 1; k < a[i]; a[i] -= k, (k <<= 1)) 
            for (int j = sum, tmp = i*k j >= tmp; --j) {
                if (dp[j-tmp] + tmp > j)
                    continue;
                dp[j] = mmax(dp[j], dp[j-tmp] + tmp);
            }
        for (int j = sum, tmp = a[i]*i; j >= tmp; --j) {
            if (dp[j-tmp] + tmp > j)
                continue;
            dp[j] = mmax(dp[j], dp[j-tmp] + tmp);
        }
    }
    if (dp[sum] == sum)
        return true;
    else 
        return false;
}
int main() {
    freopen("out.t", "w", stdout);
    freopen("in.t", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    for (T = 1; getin(); ++T) {
        cout << "Collection #" << T << ":" << "\n";
        if (judge()) 
            cout << "Can be divided." << "\n\n";
        else 
            cout << "Can't be divided." << "\n\n";
    }
    return 0;
}

2017-09-08

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值