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/

noi.openjudge的题目

NOI OpenJudge 1.13.19 啤酒厂选址 代码查错与疑问

整理小朋友在noi.openjudge上的作业

2971 抓住那头牛 noi.openjudge

noi openjudge 1768:最大子矩阵