题目链接
http://codeforces.com/contest/687/problem/C
思路
第一次遇到这种状态表示的dp,转移还是0-1背包,但是状态表示非常重要
可以这样考虑:我们有一个集合A,
sum{A}=K
,现在我们从集合A里面选出若干元素,组合一个新的集合B,另
sum{B}=j
对于初始的大集合S里面的每个元素i,我们可以做的就是:
1. 不选入A以及B
2. 选入A但是不选入B
3. 选入A且选入B
那么就可以由此推断出我们的状态表示
d[i][j][k]
:前i个元素,选入集合A中的和为k,选入集合B中的和为j是否存在
转移方程:d[i][j][k]=d[i−1][j][k]|d[i−1][j][k−ci]|d[i−1][j−ci][k−ci];
细节
- 滚动数组优化空间
- 目标: d[i][j][K],其中j≤K
代码
#include <bits/stdc++.h>
using namespace std;
inline int in() {int x; scanf("%d", &x); return x;}
#define pr(x) {cout << #x << ' ' << x << endl;}
const int maxn = 505;
int d[2][maxn][maxn], vis[maxn];
int n, K, c[maxn], tot = 0;
int main() {
n = in(); K = in();
for (int i = 1; i <= n; i++) c[i] = in();
d[0][0][0] = 1;
for (int i = 1, t = 0; i <= n; i++, t ^= 1) {
for (int j = 0; j <= 500; j++) {
for (int k = 0; k <= 500; k++) {
d[t ^ 1][j][k] = d[t][j][k];
if (k >= c[i]) d[t ^ 1][j][k] |= d[t][j][k - c[i]];
if (k >= c[i] && j >= c[i]) d[t ^ 1][j][k] |= d[t][j - c[i]][k - c[i]];
if (d[t ^ 1][j][k] && k == K && j <= K)
vis[j] = 1;
}
}
}
for (int i = 0; i <= K; i++)
if (vis[i]) tot++;
printf("%d\n", tot);
for (int i = 0; i <= K; i++)
if (vis[i])
printf("%d ", i);
return 0;
}