1. 程式人生 > >藍橋杯 2017_9 包子問題

藍橋杯 2017_9 包子問題

第八題

標題:包子湊數 小明幾乎每天早晨都會在一家包子鋪吃早餐。他發現這家包子鋪有N種蒸籠,其中第i種蒸籠恰好能放Ai個包子。每種蒸籠都有非常多籠,可以認為是無限籠。 每當有顧客想買X個包子,賣包子的大叔就會迅速選出若干籠包子來,使得這若干籠中恰好一共有X個包子。比如一共有3種蒸籠,分別能放3、4和5個包子。當顧客想買11個包子時,大叔就會選2籠3個的再加1籠5個的(也可能選出1籠3個的再加2籠4個的)。 當然有時包子大叔無論如何也湊不出顧客想買的數量。比如一共有3種蒸籠,分別能放4、5和6個包子。而顧客想買7個包子時,大叔就湊不出來了。 小明想知道一共有多少種數目是包子大叔湊不出來的。 輸入 ---- 第一行包含一個整數N。(1 <= N <= 100) 以下N行每行包含一個整數Ai。(1 <= Ai <= 100) 輸出 ---- 一個整數代表答案。如果湊不出的數目有無限多個,輸出INF。 例如, 輸入: 2 4 5 程式應該輸出: 6 再例如, 輸入: 2 4 6 程式應該輸出: INF 樣例解釋: 對於樣例1,湊不出的數目包括:1, 2, 3, 6, 7, 11。 對於樣例2,所有奇數都湊不出來,所以有無限多個。 資源約定: 峰值記憶體消耗(含虛擬機器) < 256M CPU消耗 < 1000ms 請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入...” 的多餘內容。 注意: main函式需要返回0; 只使用ANSI C/ANSI C++ 標準; 不要呼叫依賴於編譯環境或作業系統的特殊函式。 所有依賴的函式必須明確地在原始檔中 #include <xxx> 不能通過工程設定而省略常用標頭檔案。 提交程式時,注意選擇所期望的語言型別和編譯器型別。

對於這題,參考了很多答案,TMD竟然都是一個模子刻出來的,都是大佬,自己真心不懂啊,最後勉強理解一點(1)對於其他人說的定理:只有所有數互質的時候,才可能使得不能由這些數表示出的數為有限個,換句話說,就是如果不滿足所有數的最大公約數為1,那麼就會有無窮多個數不能由這些數表示出來。這個應該基於 ax+by = gxd(a,b) 基礎上推出來的,但是嚴格證明,我不會啊~~~(2)第二點就是多個數的公約數滿足交換律,並且可以直接迭代。例如a,b,c的最大公約數就是先求兩個得公約數g,再求g與剩下的公約數,更多的數類似(這裡就不證明了)(3)就是涉及關鍵的標記哪些數可以由組合產生,看了網上的都說揹包啥的,但是此時我並不太懂揹包啊,所以就自己試著理解了一下那個迴圈,初始條件dp[0] = 1,表示0可以由組合產生,剩下就是關鍵了,以組合,2,3,5為例,先是從2開始,dp[2] = d[4] = dp[6] = dp[8] .... = 1這些組合表示了1*2,2*2,3*2,4*2.....然後來到第二個迴圈,(我們最終想要知道是否能夠任意的ax+by+cz = n,即能否各種係數組合),第二個迴圈到了3,我們知道dp[2] = 3,在此時仍然出現dp[2+3] = 1,dp[2*2+3] = 1等等,表示可以多倍的2 + 1倍的3.再來看看能否多倍的3加一倍的2,由第一次迴圈dp[2]= 1 ,此後有dp[2] = dp[2+3] = dp[2+3+3] = 1這些組合都是有1*2 + p*3 即一倍的2和多倍的3組合(加上5同理,這裡不列舉了)所以這個二層迴圈存在的意義就是把各種係數組合得到的數都標記了,表示可以有2,3,5組合得到的數。

推廣到任意的n時候,就可以得到任意給定為籠子中包子數目可以組成的數了。。。。

#include<cstdio>
#include<cstring>
#define MAX 10000+5
int gcd(int a, int b){
	return b ? gcd(b,a%b) : a;
}

int dp[MAX];
int a[100+5];
int main()
	{
		int n;
		scanf("%d",&n);
		memset(dp,0,sizeof(dp));
		for(int i = 0; i < n; ++i){
			scanf("%d",&a[i]);
		}
		int g = a[0];
		for(int i = 1; i < n; ++i){
			g = gcd(g,a[i]);
		}
		if(g != 1){
			printf("INF\n");
			return 0;
		}
		dp[0] = 1;
		for(int i = 0; i < n; ++i){
			for(int j = 0; j + a[i] < MAX ; ++j){
				if(dp[j])
					dp[j+a[i]] = 1;
			}
		} 
		int ans = 0;
		for(int i = 0; i < MAX; ++i){
			if(!dp[i])
				++ans;
		}
		printf("%d",ans);
		
		return 0;
	}