HDU 1059 DP (完全背包 && 多重背包)

题意 : 给你6堆钻石,每一堆钻石的价值为 i 每一堆钻石有 i 个 问你能不能平分

题解 : 这种问题一般来说应该是dp… 当然这个也不例外。dp (i) 表示 能不能用这么多钻石表示出 i 这么多的价值 dp 的取值为 0 / 1 表示不可以和可以。这样的话我们就可以进行转移了

如果dp (i) = 1 的时候我们才进行转移 通过这种状态转移到后面的所有可能状态 dp (i + k * j) = 1; 这种转移 注意 :::当我们在转移的过程中发现如果有一个值已经被设置成1的时候,后面的一定已经都被设置成1了 (因为我们每次都是转移的一个相同的数);具体看代码吧 (注意转移的方向非常重要,是从大到小还是从小到大要考虑好)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;
const int maxn = 120005;
int dp[maxn] = {0};
int num[7] = {0};

int main () {
    ios_base :: sync_with_stdio(false);
    int cas = 1;
    while (cin >> num[1] >> num[2] >> num[3] >> num[4] >> num[5] >> num[6]) {
        if (!(num[1] + num[2] + num[3] + num[4] + num[5] + num[6])) break;
        cout << "Collection #"<< cas ++ << ":" << endl;
        memset (dp,0,sizeof(dp));
        int sum = 0;
        for (int i = 1;i <= 6; ++ i) {
            sum += (i * num[i]);
        }
        if (sum % 2) {
            cout << "Can't be divided." << endl;
            cout << endl;
            continue;
        }
        int u = sum / 2;
        dp[0] = 1;
        int mx = 0;
        for (int i = 1;i <= num[1]; ++ i) dp[i] = 1;
        mx = num[1];
        for (int i = 2;i <= 6; ++ i) {
            for (int j = mx;j >= 0;-- j) { // 防止选重复了
                if (dp[j]) {
                    for (int k = 1;k <= num[i] && k * i + j <= u; ++ k) {
                        if (dp[j + k * i]) break; // 因为每次都是加上 i 并且顺序是从后往前 所以这个可以直接break掉就好了这个剪枝的力度是非常大的可以考虑一下,相当于我所有的k ++ 的总次数都不会超过 num[i] * i 这个数量级 相比于 mx * num[i] 不知道高到哪里去了 
                        dp[j + k * i] = 1;
                    }
                }
            }
            mx = mx + num[i] * i;
        }
        if (dp[u]) {
            cout << "Can be divided." << endl;
        }
        else {
            cout << "Can't be divided." << endl;
        }
        cout << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值