OpenJudge NOI 4976 硬币
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenJudge NOI 4976 硬币相关的知识,希望对你有一定的参考价值。
http://noi.openjudge.cn/ch0207/4976/
描述
宇航员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)输出第一行是一个整数,即有多少种硬币是必须被使用的。
第二行是这些必须使用的硬币的面值(从小到大排列)。
最朴素的方法是枚举每一种硬币,去掉这种硬币来计算X是否可达。这种n**3的算法会超时。
我们假设 f(x) 是用所有种类钱币组成 x 的方案数。假设 a[i] 是第 i 种硬币的价值。
f(x - a[i]) = (用到 a[i] 凑出价值 x - a[i] 的方案数) + (没用到 a[i] 凑出 x - a[i] 的方案数)
f(x)= (用到 a[i] 凑出价值 x 的方案数) + (没用到 a[i] 凑出 x 的方案数) = (没用到 a[i] 凑出 x - a[i] 的方案数) + (没用到 a[i] 凑出 x 的方案数)
我们再假设 g(x, i) 是没用到 a[i] 凑出 x 的方案数。
f(x) - f(x - a[i]) = (没用到 a[i] 凑出 x 的方案数) - (用到 a[i] 凑出价值 x - a[i] 的方案数) = g(x, i) - (f(x - a[i]) - g(x - a[i], i))
于是 f(x) = g(x, i) + g(x - a[i], i)
如果 g(x, i) = 0,即没用到 a[i] 凑出 x 的方案数是 0 的话,那么说明 a[i] 是必须被用到的。
我们只需要计算 g(x, i) = f(x) - f(x - a[i]) + f(x - 2 * a[i]) - ....
f(0) = 1,f(x) = 0 (x < 0)
于是我们只需要计算出 f(x) 就可以了,然后对于每个 i,判断 g(X, i) 是否为 0。
#include <iostream> #include <cmath> #include <queue> #include <vector> #include <limits.h> #include <algorithm> using namespace std; int X, coin[220] = {}; vector<int> v; int calc(int f[], int i) { int ret = f[X]; for (int j = 1;; ++j) { if (X - j * coin[i] < 0) { break; } int c = (j % 2 == 0 ? 1 : -1); ret += c * (f[X - j * coin[i]]); } return ret; } int main () { int n; cin >>n >>X; int f[100007] = {}; for (int i = 0; i < n; ++i) { cin >>coin[i]; } f[0] = 1; for (int i = 0; i < n; ++i) { for (int j = X; j >= coin[i]; --j) { f[j] += f[j - coin[i]]; } } int cnt = 0; for (int i = 0; i < n; ++i) { if (calc(f, i) == 0) { ++cnt; v.push_back(coin[i]); } } cout <<cnt <<endl; int sz = v.size(); for (int i = 0; i < sz; ++i) { cout <<v[i]; if (i == sz - 1) { cout <<endl; } else { cout <<" "; } } }
以上是关于OpenJudge NOI 4976 硬币的主要内容,如果未能解决你的问题,请参考以下文章
求代码 http://noi.openjudge.cn/ch0104/05/