这道题显然是一个多重背包问题,用表示价值为的状态是否可达.
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]];
但是,这样的时间复杂度过高.我们需要进行优化,当然,我们可以采用二进制或者单调队列的形式优化多重背包,降低时间复杂度.但是在这道题中,我们用另外一种方式优化多重背包.
由于题目要求的是可达性,而不是最优性.我们就没有必要遍历整个状态空间了.我们只要关注可达的状态空间即可.
我们假设大理石价值总和为,我们就要求的可达性.
对于一个状态,若该状态可达,那么分为两种情况:
1.状态在 阶段之前就已经可达了,那么第 阶段也一定可达.
2.可达,第 个阶段时增加一块价值为 的大理石后, 也一定可达.
我们用表示达到 状态需要用的第 种大理石的块数.若 在 阶段前可达, 我们不用进行状态转移, 反之, 若 可达, 判断是否小于等于 , 然后进行状态转移.并且在状态转移的过程中,我们尽可能采取第一种转移方案.
代码实现如下:
#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';
}
}