总时间限制: 1000ms 内存限制: 262144kB
描述
宇航员Bob有一天来到火星上,他有收集硬币的习惯。于是他将火星上所有面值的硬币都收集起来了,一共有n种,每种只有一个:面值分别为a1,a2… an。 Bob在机场看到了一个特别喜欢的礼物,想买来送给朋友Alice,这个礼物的价格是X元。Bob很想知道为了买这个礼物他的哪些硬币是必须被使用的,即Bob必须放弃收集好的哪些硬币种类。飞机场不提供找零,只接受恰好X元。
输入
第一行包含两个正整数n和x。(1 <= n <= 200, 1 <= x <= 10000)
第二行从小到大为n个正整数a1, a2, a3 … an (1 <= ai <= x)
输出
第一行是一个整数,即有多少种硬币是必须被使用的。
第二行是这些必须使用的硬币的面值(从小到大排列)。
样例输入
5 18 1 2 3 5 10
样例输出
2 5 10
提示
输入数据将保证给定面值的硬币中至少有一种组合能恰好能够支付X元。
如果不存在必须被使用的硬币,则第一行输出0,第二行输出空行。
容斥定理
在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
简单来说,对于一种钱,如果没有它,还可以达到想要的钱,它就是不必须的,用01背包写。
AC代码
#include<stdio.h> #include<string.h> int dp[1000005];//表示形成i钱数的方案 int ans[1000005];//表示没有j时形成i钱数的方案数,如果方案数>0.那说明j不必要 int a[220];//存放钱的种类 int b[220];//存放必须有的钱币的种类 int main() { int n, m;//钱的种类数和礼物价格 int count, k; while (scanf("%d%d", &n, &m) != EOF) { for (int i = 1; i <= n; i++) scanf("%d", &a[i]);//读入钱币 memset(dp, 0, sizeof(dp));//初始化dp,即价格为i时可用的方案数 dp[0] = 1;//0元礼物的方案数为1 for (int i = 1; i <= n; i++) { for (int j = m; j >= a[i]; j--)//逆序,典型的0-1背包 { dp[j] = dp[j] + dp[j - a[i]]; //j是由a[i]和j-a[i]的和,a[i]的方案为1, //j-a[i]的方案数为dp[j-a[i]]; } } count = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j <= m; j++) { if (j < a[i]) ans[j] = dp[j]; else ans[j] = dp[j] - ans[j - a[i]]; } if (ans[m] == 0)//缺了j就不行了,那么j是必需的 { b[count++] = a[i]; } } printf("%d\n", count); if (count == 0) printf("\n\n"); else { for (int i = 0; i < count; i++) { if (i != count - 1) printf("%d ", b[i]); else printf("%d\n", b[i]); } } } }