洛谷 P5020 货币系统

题目描述

在网友的国度中共有$ n $种不同面额的货币,第 i种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为\(n\)、面额数组为 \(a[1..n]\)的货币系统记作$ (n,a)$。

在一个完善的货币系统中,每一个非负整数的金额 \(x\) 都应该可以被表示出,即对每一个非负整数 \(x\),都存在 \(n\) 个非负整数\(t[i]\)满足 \(a[i] \times t[i]\) 的和为$ x$。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 \(x\) 不能被该货币系统表示出。例如在货币系统 \(n=3\),\(a=[2,5,9]\)中,金额\(1,3\) 就无法被表示出来。

两个货币系统$ (n,a)$和 \((m,b)\) 是等价的,当且仅当对于任意非负整数 \(x\),它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 \((m,b)\),满足$(m,b) $与原来的货币系统 $(n,a) $等价,且 \(m\) 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 \(m\)


输入输出格式

输入格式:

输入文件的第一行包含一个整数 \(T\),表示数据的组数。

接下来按照如下格式分别给出 \(T\) 组数据。 每组数据的第一行包含一个正整数 \(n\)。接下来一行包含 \(n\) 个由空格隔开的正整数 \(a[i]\)

输出格式:

输出文件共有 \(T\)行,对于每组数据,输出一行一个正整数,表示所有与$(n,a) $等价的货币系统 \((m,b)\)中,最小的 \(m\)


输入输出样例

输入样例#1:

2
4
3 19 10 6
5
11 29 13 19 17

输出样例#1:

2
5


说明

在第一组数据中,货币系统$ (2, [3,10])$和给出的货币系统 $(n,a) $等价,并可以验证不存在 $m < 2 $的等价的货币系统,因此答案为 \(2\)。 在第二组数据中,可以验证不存在\(m<n\) 的等价的货币系统,因此答案为$ 5$。


【数据范围与约定】

对于\(100%\)的数据,满足\(1 ≤ T ≤ 20\), \(n\),\(a[i] ≥ 1\)


思路

先解释一下样例1中第一组数据的3,10,19,6等价于3,10的原因

6=3+3,19=3+3+3+10

即13,19都可以被凑出来

而第二组中的5个数都不能将其他的数凑出来(或被凑出来)

所以直接输出了5

所以就排序看看能不能凑出来就好啦

a[i]=0表示没有i这个数,a[i]=1表示可以凑出i这个数,a[i]=2表示本身就有i这个数

如果处理完成之后a[1~n]中还有等于2的(即本身就有这个数并且不能凑(只能他凑别人))

就让ans++,最后输出就好了

(nb题目毁我人生,差点没读懂)

(话说现在做的题目的题面都好难懂啊,小小蒟蒻做不出来啊......)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#define INF 0x3f3f3f3f
using namespace std;

int a[25001];
int b[101];
int t,n,ans=0;

inline int read() {
    char c=getchar();
    int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} 
    while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    return x*f;
}

int main() {
    //freopen("money.in","r",stdin);
    //freopen("money.out","w",stdout);
    t=read();
    while (t--) {
        ans=0;
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        for (int i=1; i<=n; i++) {
            b[i]=read();
            a[b[i]]=2;//本身就有b[i]这个数;
        }
        sort(b+1,b+1+n);//从小到大排序 
        for (int i=1; i<=b[n]; i++) {
            if(a[i]>0) {//如果可以凑出i
                        //那么也可以凑出i+b[j] 
                for(int j=1; j<=n; j++) {
                    if(i+b[j]<=b[n])//排序之后,b[n]一定是b数组中最大的数,在这里防止越界 
                        a[i+b[j]]=1;
                    else break;//越界的话直接退出 
                }
            }
        }
        for(int i=1; i<=b[n]; i++)
            if(a[i]==2) ans++;//统计a[i]==2的个数输出 
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/loceaner/p/10788416.html
今日推荐