• P5020 货币系统 题解


    CSDN同步

    原题链接

    简要题意:

    求一个长度最小的货币系统与给出的货币系统等价。求这个货币系统的长度。等价的定义详见题目,不再赘述。

    本文可能用到一些集合论,请放心食用。

    算法一

    (n=2) 时,只需判断两个数的倍数关系。有倍数关系则答案为 (1),否则为 (2).

    时间复杂度:(O(T imes n)).

    实际得分:(15pts).

    算法二

    (n=3) 时,首先,如果两个数都是另一个数的倍数,那么答案为 (1).

    否则,如果仍存在倍数关系,则答案为 (2).

    其余的情况,只需要考虑,最小的数和次小的数能否表示出最大的数。

    能则为 (2),否则为 (3).

    这里有很多种方法判断。比如:

    1. 暴力,用桶直接来,(O(max a_i)).

    2. 考虑解方程,用 ( exttt{exgcd}) 写,(O(log max a_i)).

    总之,时间复杂度为 (O(T imes n imes (max a_i))). (无需优化,因为没有必要)

    实际得分:(30pts).

    算法三

    首先,假设给出 (S),答案为 (T). 则必有:

    [T in S ]

    下面来证明这个结论。

    如果 (T ot in S),则考虑取出 (x = min (i in S))(y = min(i in T)).

    如果 (x<y),则 (x) 无法被表示。

    如果 (y>x),则 (y) 无法被表示。

    如果 (x=y),那么不断递归下去,得证。

    所以,我们只需要在给出的货币系统中寻找答案即可。

    这里我们枚举选的答案,然后一一验证。

    时间复杂度:(O(2^n imes n imes (max a_i) imes T)),可以通过。

    实际得分:(65pts).

    算法四

    注: (n = 25),本人没有想到什么可以 (O(2^n imes T)) 过掉的算法,因此这一档部分分可能是用来给选手瞎搞的。???

    你发现不需要一一枚举。首先你排序一下。

    你只要用当前已有的数,判断当前正在决策的这个数能否被表示出。

    能,那么说明这个数没有必要,把它抛弃。

    否则,肯定要选。只是因为,(>) 它的数表示不出,而 (<) 也表示不出,只有它自己能表示自己,所以必须选。

    那么,这样我们可以唯一确定一个数是否被选。(排序后从小到大选)

    如何判断?

    我们发现,每次新加入一个数 (x) ,我们需要维护能判断出的桶。

    此时,可以在原有的桶上,对每个能表示出的数 (k),把 (k+x , k+2x,k+3x cdots k+ infty x) 全部标为可以判断。这是显然的。

    至于 (infty) 的上限,只要标到 (max a_i) 即可,因为后面的数没有用了。

    时间复杂度:(O(n imes T imes (max a_i))).

    实际得分:(100pts).

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=5e4+1;
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    int T,n,a[N];
    int cnt=0;
    int maxi; bool h[N];
    
    int main(){
    	T=read(); while(T--) {
    		n=read(); maxi=0; cnt=0;
    		memset(h,0,sizeof(h)); //初始化
    		for(int i=1;i<=n;i++) a[i]=read(),maxi=max(maxi,a[i]);
    		sort(a+1,a+1+n); //排序
    		for(int i=1;i<=n;i++)
    			if(!h[a[i]]) { //不能被表示
    				h[a[i]]=1; cnt++;
    				for(int j=1;j<=maxi;j++)
    					if(h[j]) {
    						for(int k=a[i];j+k<=maxi;k+=a[i])
    							h[j+k]=1;
    					} //维护能被表示的桶
    			} printf("%d
    ",cnt);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    hdu 1018
    hdu 1005
    hdu 1222
    hdu 1297
    hdu 1568
    WCF入门, 到创建一个简单的WCF应用程序
    BarTender 通过ZPL命令操作打印机打印条码, 操作RFID标签
    WCF入门的了解准备工作
    C# Bartender模板打印 条码,二维码, 文字, 及操作RFID标签等。
    Qt configure脚本说明
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12576482.html
Copyright © 2020-2023  润新知