凑硬币

:题目名称: 凑硬币

题目描述

jyb 想给他的女朋友买一块很贵的巧克力,jyb 有 n个硬币,第 i 个硬币的价值为 ci 元,巧克力的价格为 k。jyb 想用一些硬币凑出恰好 k元为他的女朋友买一块巧克力。

看着他的硬币,他想到了一个问题,假设他不给女朋友巧克力,而是给她 k元的硬币,她用这恰好 k元的硬币可以凑出哪些价值呢?他想不出来,于是请你帮他解答这个问题。

更准确地说,jyb 想要知道所有的价值 x,在 jyb 的硬币中能够找出一个恰好 k 元的子集 S,使得存在 S 的子集 S2,S2硬币价值的和是 x。显然 0和 k都是符合条件的 x。


Input:

6 18
5 6 1 10 12 2

Output:

16
0 1 2 3 5 6 7 8 10 11 12 13 15 16 17 18


Solution

由于恰好K元的子集可能有多个, 且题目要求只能使用同一子集中的元素凑硬币
所以设 dp[j][t]==truedp[j][t] == true 表示在满足恰好jj元的若干子集中, 可以由其中一个集合中元素凑出tt

dp[j+A[i]][t+A[i]]=dp[j][t]dp[j+A[i]][t+A[i]] |= dp[j][t]
dp[j+A[i]][t]=dp[j][t]dp[j+A[i]][t] |= dp[j][t]
t[0,j],j[0,KA[i]]t ∈ [0, j], j ∈ [0, K-A[i]]


Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 1005;

int N, K;
int A[maxn], dp[maxn][maxn];

int main(){
        freopen("coin.in", "r", stdin);
        freopen("coin.out", "w", stdout);
        
        scanf("%d%d", &N, &K);
        for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]);
        dp[0][0] = 1;
        
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = K; j >= 0; j --)
                        for(reg int t = j; t >= 0; t --){
                                dp[j+A[i]][t+A[i]] |= dp[j][t];
                                dp[j+A[i]][t] |= dp[j][t];
                        }
        
        /*
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = K+A[i]; j >= A[i]; j --)
                        for(reg int t = j; t >= A[i]; t --)
                                dp[j][t] |= dp[j-A[i]][t-A[i]],
                                dp[j][t] |= dp[j-A[i]][t];
        */

        int cnt = 0;
        for(reg int i = 0; i <= K; i ++) cnt += dp[K][i];
        printf("%d
", cnt);
        for(reg int i = 0; i <= K; i ++) 
                if(dp[K][i]) printf("%d ", i);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822676.html