题意
给你n个硬币,一个数k,
n个硬币每个硬币都有一个价值Ci,
问从这些硬币中组合成k的集合的子集合中又能组成哪些数?
6 12 ===> [0, 6, 12, 18]
1 5 12 ===> [0, 1, 5, 6, 12, 13, 17, 18]
1 2 5 10 ===> [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18]
2 6 10 ===> [0, 2, 6, 10]
ans ===> [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18]
题解
【01背包扩展】
状态表示:
dp[i][j]: 数值为 i时,j 是否能被组成
属性:
cnt
集合划分:
[考虑出边]
(i, j) ---> (i + a[i], j)
(i, j) ---> (i + a[i], j + a[i])
初始状态:
dp[0][0] = true;
注意:
之后就是 01 背包了, [注意逆序循环]
Code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define Buff ios::sync_with_stdio(false)
#define mem(a) memset(a, 0, sizeof a)
const int N = 1e3 + 7;
bool dp[N][N];
int ans[N];
signed main()
{
Buff;
int n, k;
while(cin >> n >> k)
{
int cnt = 0;
dp[0][0] = 1;
for(int nn = 1; nn <= n; nn++)
{
int x; cin >> x;
for(int i = k - x; i >= 0; i--)
for(int j = 0; j <= i; j++)
if(dp[i][j])
dp[i+x][j] = dp[i+x][j+x] = 1;
}
for(int j = 0; j <= k; j++)
if(dp[k][j]) ans[++cnt] = j;
cout << cnt << "\n";
for(int i = 1; i <= cnt; i++) cout << ans[i] << " ";
cout << "\n";
}
return 0;
}