AcWing 318. 划分大理石 (多重背包 + 可行性优化)

这道题显然是一个多重背包问题,用f(j)表示价值为j的状态是否可达.

for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= c[i]; ++j)
        for(int k = m; k >= a[i]; --k)
            f[k] |= f[k - a[i]];

但是,这样的时间复杂度过高.我们需要进行优化,当然,我们可以采用二进制或者单调队列的形式优化多重背包,降低时间复杂度.但是在这道题中,我们用另外一种方式优化多重背包.

由于题目要求的是可达性,而不是最优性.我们就没有必要遍历整个状态空间了.我们只要关注可达的状态空间即可.

我们假设大理石价值总和为sum,我们就要求f(sum/2)的可达性.

对于一个状态f(j ),若该状态可达,那么分为两种情况:

1.f(j)状态在 i 阶段之前就已经可达了,那么第 i 阶段f(j)也一定可达.

2.f(j - v_i)可达,第 i 个阶段时增加一块价值为 v_i 的大理石后, f(j)也一定可达.

我们用used(j)表示达到 f(j) 状态需要用的第 i 种大理石的块数.若 f(j)i 阶段前可达, 我们不用进行状态转移, 反之, 若 f(j - v_i)可达, 判断used(j)是否小于等于 c_i, 然后进行状态转移.并且在状态转移的过程中,我们尽可能采取第一种转移方案.

代码实现如下:

#include <bits/stdc++.h>
// #define LOCAL
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
// #define int long long
#define debug(a) cerr << #a << "=" << a << endl;
using namespace std;
const int N = 7;
vector<int> c(N, 0), v(N, 0);
signed main(){
#ifdef LOCAL
    freopen("input.in", "r", stdin);
    freopen("output.out", "w", stdout);
#endif
    IOS;
    for (int i = 1; i <= 6; ++i)
        v[i] = i;
    while(cin >> c[1] >> c[2] >> c[3] >> c[4] >> c[5] >> c[6], accumulate(c.begin() + 1, c.end(), 0)){
        int sum = c[1] * 1 + c[2] * 2 + c[3] * 3 + c[4] * 4 + c[5] * 5 + c[6] * 6;
        int m = sum / 2;
        vector<int> used(120010, 0), f(120010, false);
        if(sum % 2){
            cout << "Can't\n";
            continue;
        }
        f[0] = 1;
        for (int i = 1; i <= 6; ++i){
            for (int j = 0; j <= m; ++j)
                used[j] = 0;
            for (int j = v[i]; j <= m; ++j)
                if(!f[j] && f[j - v[i]] && used[j - v[i]] < c[i])
                    f[j] = true, used[j] = used[j - v[i]] + 1;
        }

        cout << (f[m] ? "Can" : "Can't") << '\n';
        
    }

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值