1. 程式人生 > >NOIP2018day1T2——貨幣系統(完全揹包/搜尋)

NOIP2018day1T2——貨幣系統(完全揹包/搜尋)

描述
在網友的國度中共有 n 種不同面額的貨幣,第 i 種貨幣的面額為 a[i],你可以假設每一種貨幣都有無窮多張。為了方便,我們把貨幣種數為 n、面額陣列為 a[1…n]的貨幣系統記作 (n,a)。
在一個完善的貨幣系統中,每一個非負整數的金額 x 都應該可以被表示出,即對每一個非負整數 x,都存在 n 個非負整數 t[i] 滿足 a[i]× 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。
樣例輸入
4
3 19 10 6
5
11 29 13 19 17
樣例輸出
2
5
提示
在這裡插入圖片描述

不知道有多少人和我一樣直接先套了個 E x g c d Exgcd 上去

不過話說回來 E

x g c d Exgcd 有65分啊

其實一眼就可以看出這是一個完全揹包,數的大小看作所佔空間,

f [ i

] f[i] 表示值為 i i 的數能否被湊出來

初始化 f [ 0 ] = 1 f[0]=1

然後就按完全揹包那樣做就是了

#include<bits/stdc++.h>
using namespace std;
#define MAXN 25005
int f[MAXN],a[105];
inline int read(){
	char ch=getchar();
	int res=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res;
}
int main()
{
	int T=read();
	while (T--)
	{
		int n=read();
		if (n==2)
		{
			int x=read(),y=read();
			if (x>y) swap(x,y);
			if (y%x==0) printf("1\n");
			else printf("2\n");
		}
		else
		{
			for (int i=1;i<=n;i++) a[i]=read();
			sort(a+1,a+n+1);
			memset(f,0,sizeof(f));
			f[0]=1;
			int ans=0;
			for (int i=1;i<=n;i++)
			{
				if (f[a[i]]) continue;
				++ans;
				for (int j=0;j+a[i]<MAXN;j++)
					if (f[j])
						f[j+a[i]]=1;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

然而考場上應該是瞎了,居然沒看出來,打了發搜尋,估摸著可能要被卡,於是寫了個很爛的記搜,

感謝 c c f ccf 少爺機,3個點 90 0 + m s 900^{+}ms

下來後改了一下,還是跑得很快的

主要因為其實要能拼起來的話會出現的數是很少的,所有其實很快

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
const int N=105;
int T,a[N],n,vis[N][25005],flag,ans;
void dfs(int pos,int res){
    if(res==0){
        flag=true;return;
    }
    if(pos==0){
        return;
    }
    if(vis[pos][res]==0)return;
    if(vis[pos][res]==1){
        flag=true;return;
    }
    if(res%a[pos]==0)flag=true;
    if(flag){
        vis[pos][res]=1;return;
    }
    for(int i=0;i*a[pos]<=res&&(!flag);i++){
        dfs(pos-1,res-i*a[pos]);
    }
    vis[pos][res]=flag;
}
int main(){
    int T=read();
    while(T--){
        n=read();memset(vis,3,sizeof(vis));ans=0;
        for(int i=1;i<=n;i++){
            a[i]=read();
        }
        sort(a+1,a+n+1);
        for(int i=2;i<=n;i++){
            flag=false;
            dfs(i-1,a[i]);
            if(flag)ans++;
        }
        cout<<(n-ans)<<'\n';
    }
}

最後
推廣一下另外幾篇題解:
DAY1T1:鋪設道路:(並查集??)
DAY1T2:貨幣系統:(完全揹包/搜尋)
DAY1T3:賽道修建:(二分答案+貪心策略)
DAY2T1:旅行:(基環樹搜尋)
DAY2T2:填數遊戲:(暴力搜尋找規律)
DAY2T3:保衛王國:(動態dp+Splay)