洛谷P5020 [NOIP2018 提高组] 货币系统 题解

洛谷P5020 [NOIP2018 提高组] 货币系统 题解

题目链接:P5020 [NOIP2018 提高组] 货币系统

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

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

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

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

输入输出样例

输入 #1

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

输出 #1

2   
5  

观察第一组数据,可以发现其等价于 3 10

因为 19 19 19 6 6 6 均可以用 3 3 3 10 10 10 替代

而第二组,每一种都是有价值的,不可替代

考虑 d p dp dp

d p [ i ] dp[i] dp[i] 表示凑出 i i i 最多需要花费的金额种类

显然这个是个完全背包。

d p [ a [ i ] ] = 1 dp[a[i]]=1 dp[a[i]]=1 则说明 a [ i ] a[i] a[i] 是有价值的面额

然后就没了,代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N (int)(3e4+15)
int Q,n,mx;
int a[115],dp[N];
signed main()
{
    read(Q);
    while(Q--)
    {
        memset(dp,0xc0,sizeof(dp));
        read(n);dp[0]=0;mx=-INF;
        for(int i=1; i<=n; i++)
        {
            read(a[i]);
            mx=max(mx,a[i]);
        }
        for(int i=1; i<=n; i++)
            for(int j=a[i]; j<=mx; j++)
                dp[j]=max(dp[j],dp[j-a[i]]+1);
        int res=0;
        for(int i=1; i<=n; i++)
            if(dp[a[i]]==1)++res;
        write(res);pc('\n');
    }
    return 0;
}

其实这个 b b b 是可以证明有 $b \subseteq a $ 的

但是,OI不需要严谨证明。OI靠得是OI直觉。

转载请说明出处

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值