藍橋杯 試題 歷屆試題 包子湊數 dp+歐幾里得算法

問題描述
  小明幾乎天天早晨都會在一家包子鋪吃早餐。他發現這家包子鋪有N種蒸籠,其中第i種蒸籠剛好能放Ai個包子。每種蒸籠都有很是多籠,能夠認爲是無限籠。


  每當有顧客想買X個包子,賣包子的大叔就會迅速選出若干籠包子來,使得這若干籠中剛好一共有X個包子。好比一共有3種蒸籠,分別能放三、4和5個包子。當顧客想買11個包子時,大叔就會選2籠3個的再加1籠5個的(也可能選出1籠3個的再加2籠4個的)。


  固然有時包子大叔不管如何也湊不出顧客想買的數量。好比一共有3種蒸籠,分別能放四、5和6個包子。而顧客想買7個包子時,大叔就湊不出來了。


  小明想知道一共有多少種數目是包子大叔湊不出來的。
輸入格式
  第一行包含一個整數N。(1 <= N <= 100)
  如下N行每行包含一個整數Ai。(1 <= Ai <= 100)
輸出格式
  一個整數表明答案。若是湊不出的數目有無限多個,輸出INF。
樣例輸入
2
45
5
樣例輸出
6
樣例輸入
2
4
6
樣例輸出
INF
樣例說明
  對於樣例1,湊不出的數目包括:1, 2, 3, 6, 7, 11。
  對於樣例2,全部奇數都湊不出來,因此有無限多個。


解題思路:咱們首先觀察第一個樣例
×表示不能湊出,√表示能湊出。觀察到8√能夠從4√+4獲得,10√能夠從5√獲得。
因此咱們得出判斷一個包子數是否能湊出的一個方法:dp解決。設dp[ j ]表示包子數爲j是否能湊出。
有 dp[ j ] |= dp[ j - A[ k ] ] ( k>0 && j - A[k] >=0 ) 
進一步觀察能夠知道,當連續√的數目>=4時,以後的數目都必定能湊出,由於只要在以前√的基礎上加上最小包子數4便可。
 
接着觀察第二個樣例: 4 6 最後樣例輸出是INF。這裏能夠證實只有當給出的包子數至少有兩個互質時最終結果不是INF。
 
  反證法:假設n個包子數都不互質,則它們的最小公約數k>1,則它們能夠表示爲
  ka1,ka2,ka3, .... kan,則相加結果爲 k( c1*a1 + c2 * a2 + ... + cn*an ) , 那麼當要湊出的數不是k的倍數的時候,必定湊不出。
  而不是k倍數的包子數有INF個.
  • 要判斷兩個數是否互質,則要計算兩個數的最大公約數是否爲1,用到了展轉相除法,附上大佬詳細證實 https://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html 
  • 而這裏dp數組的大小與兩個數最大不能組合的數有關.m,n(前提爲互質)最大不能組合的數能夠證實是 m*n - m -n 有興趣的朋友能夠看https://www.cnblogs.com/DestinHistoire/p/10632970.html

根據上面思路寫的代碼提交錯了一題,輸入96,75,100按照思路結果應該是INF,但仔細想一想html

ka1 + ka2 + ka3 +...+ ka4 = gcd( a1,a2,...,an) 的倍數,因此只須要全部包子數的公約數>1便可。(拓展歐幾里得:數組

核心思想:考慮 a1*x1+a2*x2+...+an*xn=gcd(a1,a2,...an),只能湊出來gcd的倍數)spa


 

實現代碼:code

#include<cstdio>
#include<algorithm>
using namespace std;

const int Max_N = 100;
const int Max_K = 10000;//最大不能組合的數不會超過100*100

//輸入
int N;
int A[Max_N];

bool dp[Max_K+1];//dp[] 全局變量初值爲false 固然最好使用前初始化 

//展轉相除法 
int gcd(int a,int b)
{
    return a%b==0 ? b : gcd(b,a%b);
} 

void solve()
{
    //先判斷是否爲INF 
    int k = A[0];
    for( int i=1; i<N; i++)
    {
        k = gcd( k, A[i] );//求全部包子數的最大公約數 
    }
    if( k>1 ){
        printf("INF\n");
        return;
    }
    
    sort(A,A+N);
    dp[0] = true;
    for(int i=1; i<=Max_K; i++)
    {
        for(int k=0; k<N&&A[k]<=i; k++)
        {
            if( dp[ i-A[k] ] )
            {
                dp[i] = true;
                break;
            }
        }
    }
    
    int ans=0, cnt = 0;
    for(int i=1; i<=Max_K; i++)
    {
        if( dp[i] )
        {
            cnt++;
            if( cnt==A[0] )    break;//連續√ 
        }
        else
        {
            cnt = 0;
            ans++;//不能湊的包子數    
        } 
    }
    printf("%d\n",ans);
}

int main()
{
    scanf("%d",&N);
    for(int i=0; i<N; i++)
    {
        scanf("%d",&A[i]); 
    }
    
    solve();
    
    return 0;
}
相關文章
相關標籤/搜索