組合數學及其應用——計數問題


 問題一:將一個2003邊形的每個頂點染成紅、藍、綠三種顏色之一,使得相鄰頂點的顏色互不相同,請問有多少種滿足條件的方法?

 

  分析:直接求解似乎不太現實,將多邊形的邊數看成變量,我們設置T(n)記錄方案數,應用簡單的組合計數原理,容易看到T(3) = 6 , T(4) = 18。基於這么有限的條件,我們如何求解T(2003)呢?遞推似乎是解決這種問題的好方法。

  我們考慮基於T(n-1),在第n-1后面再加一個點形成n邊形。

  如果第1個點和第n-1個點同色,對於第n個頂點我們有2種選擇,而對於剩余n-1個頂點,我們基於n-2個頂點的方案數,每次添加一個和第一個頂點相同顏色的點,便完成了所有情況的構造,此時即得到2T(n-2)種方案。

  如果第1個點和第n-1個點異色,那么對於第n個頂點我們只有一種方案,便基於T(n-1)的基礎上添加一個點即可,我們得到T(n-1)種方案。

  由此我們可以得到結論:T(n) = T(n-1) + 2T(n-2),結合初始條件,T(2003)顯然是可求的。

  如果說我們有台計算機,那么基於上面的條件其實也就可以求解了,但如果沒有的話,我們則需要進一步從這個遞推式中挖掘信息。

  外延一種根據遞推式求解通項的方法:對於遞推式a(n+2) = pa(n+1) + qa(n),a1 = α,a2 = β,x^2 + px + q = 0是其特征方程的根,x1、x2為特征方程的解。當x1 != x2,an = Ax1^(n-1) + Bx2^(n-1) ;而當x1 = x2 , an = (A + Bn)x^(n-1)。其中A和B通過初始條件a1、a2的值,利用待定系數的方法求解。

  那么考察此題給出遞推等式,我們可得出an的通項,T(2003)也不難求解了。

 

  問題二:設n是正整數。若由n個正整數組成的數列(可以相同)滿足:對於每個正整數k(k >= 2),當k在此數列中時,k - 1也在此數列中,且k-1第一次出現的位置在k最后一次出現的位置的前面,則稱該數列為“滿的”。問:對於每個整數n,有多少個滿的數列?

 

  分析:由題目鑒定,我們可知對於r = max{a1,a2……an},則區間[1,r-1]的整數都在該數列當中,我們設置集合Si來表示數列中第k個元素ak = i的下標k,即Si = {k | ak = i},之所以定義這個集合,是為了方便利用題設條件,由此我們可知min Si-1 < max Si , 則此時我們可以對Si所含元素進行一個簡單的排序。

  S1 = {b1,b2,……b(k1)}    (b1 > b2……>b(k1) 且 b(k1) > b(k1 + 1))

  S2 = {b(k1+1) , b(k1 + 2)……b(k2)}  (b(k1+1) > b(k1+2)……>b(k2)且b(k2) > b(k2 + 1))

  ……

  由此我們考察S1、S2……Sr不難發現,r個集合的n個元素與{1,2,3……n}滿足一個雙射關系,那么基於這種雙射關系,我們通過{1,2,3……n}形成n!種排列,然后相應的去匹配ak對應的正整數,便可構造出滿的數列,由此不難得出該題的結論,對於每個n,有n!個滿的數列。

 

  問題三:桌子上有六個空錢包,將12枚2元硬幣放入這些錢包中,至多剩余一個錢包,問有多少種方法?

 

  分析:這算是組合計數問題里面比較基礎的問題了。題設所說至多剩余一個,即分為沒有空錢包和剩余一個空錢包,我們只需要分情況討論然后將兩種情況的結果相加即可。

  ①沒有空錢包,這種情況對應着將6個硬幣放入6個錢包(允許有空錢包)的情況,因此在給6個錢包分組的時候,會形成11個位置,分成6組需要選擇5個位置,即C(11,5)。

  ②如果有一個空錢包,首先要選出這個空錢包,即C(6,1),將12個硬幣放入5個錢包,對應着將7個硬幣放入5個錢包(允許有空錢包)的情況,采取①的思路,我們得到C(13,4)。

  綜合起來,這道題的解即為C(11,5) + 6C(13,4)。

 

  問題四:設六邊形ABCDEF是邊長為1的正六邊形,O是六邊形的中心,除了六邊形的每一條邊,可以從O到每個頂點連一條線段,共得到12條長度為1的線段,一條路徑是指從O出發,沿着線段最后又回到O,問:長度為2003的路徑共有多少條?

 

  分析:首先我們應該考慮的是求解這道問題的通式,我們設置a[n]記錄長度為n的路徑,而b[n]表示從某個頂點出發,到達O的路徑的長度。考察該幾何圖形,我們容易看到如下的遞推關系式。

  a[n] = 6b[n-1] (考察a[n]路徑,去掉其最后一步,得到此遞推式)

  b[n] = a[n-1] + 2b[n-1](考察b[n]路徑,去掉其最后一步,得到遞推式)

  由此我們不難得出關於a[n]的遞推式——a[n+2] - 2a[n+1] - 6a[n] = 0。同樣,如果這是一道編程問題,我們基於這個遞推式就可以求解了,但是如果是個數學題的話,我們則需要給予這個遞推式進一步求其通式,其方法在問題一種已經給出。

  此題的關鍵是尋求這種遞推關系,而尋求遞推關系的關鍵則是基於一種狀態,通過去掉最后一個步來建立起與前一種狀態的聯系,而所謂的”去掉最后一步“是否完成的足夠全面(沒有重復沒有漏),則是遞推關系是否正確的關鍵一環。

 

  問題五:將n(n>=1)個有標記的球分別分配給九個人A、B、C、D、E、F、G、H、I,問:有多少種分配方法,使得A得到的球的數目與B、C、D、E一共得到的球的數目相同?

 

  分析:此題是非常考驗數學觀察力和構造能力的,既然是9個人,A球和另外四個球數量相同,我們考察(x^2 + x + x + x + x + 1 + 1 + 1 + 1)^n這樣一個式子。我們規定如果將第k個球給A,那么選取第k個因子中的x^2;如果將第k個球分給B、C、D、E,則選取第k個因子當中的第1、2、3、4個1;如果將第k個球給F、G、H、I,則選取第k個因子中第1、2、3、4個x,我們會看到,為了滿足A的球和B、C、D、E的球數和相同,需要選擇一個因子的x^2,就要對應選擇另一個因子中的某個1,而其余因子只能選擇x,由此不難看出,(x^2+4x +4)^n展開式中x^n的系數變式方案的總數。利用牛頓二項式定理,對於(x+2)^2n,x^n的系數為2^nC(2n,n)。

  這道題目呈現出來的方法思想,對於學過生成函數算法的讀者來說可能比較熟悉。即一種將組合計數和函數表達式聯系在一起,通過證明函數與組合計數的等效轉化性,然后基於函數進行運算。

 

  問題六:對所有是數字1~7的全排列的七位數,從小到大進行排序。問:數字3654217出現在第幾個?

 

  這也是一道比較基礎的組合計數問題,我們考慮第一個數字是1、2的情況,得到2*6!個數字;第一個數字是3第二位數字是1、2、4、5,得到4*5!個數字;類似地依次分析接下來的每一位,即,共有2*6! + 4*5! + 3*4! + 2*3! + 1*2! ,在此結果上+1便得到最終結果。

 

  問題七:數916238457是一個包含1~9每個數字恰好一次的九位數的例子,其還具有性質:數1~5以正常的順序出現,但1~6不以正常的順序出現。問:這樣的數有多少個?

 

  題目很簡單,關鍵點在於理解這個9位數的性質,我們先將1~5排好順序,形成6個空隙,按照性質,數字6有5個符合要求的空隙,即有5種方法,然后再依次填入7、8、9,則答案為5 * 7 * 8 * 9 = 2520.

 

  問題八:有多少種方法可將2011/2010表示成兩個n+1/n(n∈Z)型分數的乘積?(其中,ab和ba認為是一種方法)

 

  我們設2011/2010 = (p + 1)/p * (q + 1)/q,進行整理可得p = 2010 + (2010*2011)/(q - 2010) , q = 2010 + (2010*2011)/(p-2010)。由因為q、p都是正整數,所以p - 2010是2010*2011的因子即可,因此對其進行素因子分解,有2010*2011 = 2 * 3 * 5 * 67 *2011 , 其因子數共有C(5,1) + C(5,2) + C(5,3) + C(5,4) + C(5,5) = 2^5,由於p、q表示形式上的對稱性,當存在一組解(p,q)的時候,另外一組解(q,p)也會滿足條件,因此這里共有16種方法。

 

  問題九:令f(n)為滿足4x+3y+2z = 2000 = n的正整數數對(x,y,z)的個數,求f(2009) - f(2000)。

 

  設4x+3y+2z = 2000 , 觀察到2009 - 2000 = 9 , 並且y的系數3是9的因子,因此我們構造x' = x , y' = y + 3 ,z' = z,則有4x' + 3y' + 2z' = 2009,因此我們可以看到在y'的解空間中,y' > 3的時候與y的解空間形成雙射,即f(2009) - f(2000)的值為y' = 1 , 2 , 3時可以取的(x,y,z)的整數對的個數。

  y' = 1   =>    4x' + 2z' = 2006    =>  501組。

  y' = 2   =>    4x' + 2z' = 2003    =>  0組。

  y' = 3   =>    4x' + 2z' = 2000    => 499組。

  因此f(2009) - f(2000) = 499  + 501 = 1000.

 

  問題十:從1°、2°……179°選出三個角度使其成為非等腰三角形的三個內角,問:有多少種選法?

 

  我們設三個內角分別是x、y、z,由x + y + z = 180 , 我們能夠得到C(179,2)組解,需要知道的是這里的解的組合有次序的,即是一種排列。隨后我們需要找到等腰三角形的情況,為了好分析,我們先把等邊三角形單獨拿出來分析。x、y、z用(x,y,z)來表示。

  (60,60,60) , 1組。

  (1,1,178) 、(2,2,176)……(89、89、2) , 由於我們求總情況數的時候是一種排列,這里當然需要保持一致,共88C(3,1)組。

  求解最終的答案不要忘了去除排列性,即答案為C(179,2) - 1 - 88C(3,1) / A(3,3)。

 

  問題十一:求滿足下述條件的正整數的數目,可以被9整除,位數不超過2008,且各位數字中至少有兩位數字是9.

 

  總數:利用基本的分步乘法原理,有10^2008 - 1種。(減掉0的情況)

  其中9的倍數有(10^2008 - 1)/9種。

  現在考慮這些數字各位數字中至多有1個9的情況的數量,然后用總數減掉便可得答案。

  (1)有0位9,我們對前2007位在0~8中隨機選擇,為使這個2008位的整數被9整除,第2008位的數字是唯一確定的,因此這種情況下得到9^2007 - 1種情況.

  (2)有1位9,類似上面的過程,在這里我們得到C(2008,1)9^2006種情況。(顯然這種情況下是不會出現0的)

  因此最終滿足題意的數目共有(10^2008  - 1)/9 - (9^2007 - 1) - 2008*9^2006。

 

  問題十二:有六個紅球、三個籃球、三個黃球。將這些球放在一條直線上,假設同色的球沒有區別。試問:有多少種不同的方法,使得同色的球不相鄰?

 

  這其實是組合計數問題中的一類“不相鄰”問題,我們應該先將數量較多的球擺好,然后將顏色不同的球插入到其中。在這個問題中,我們首先將6個紅球擺好——10101010101(1代表紅球,0代表必須填充的間隔),而我們手頭還有另外的6個球,因此我們能夠知道,藍色球和黃色球至多相鄰一次。那么下面我們便開始分別討論。

  (1)藍色球和黃色球不相鄰:顯然有C(2,1)*C(6,3)種情況。

  (2)藍色球和黃色球相鄰一次:我們將相鄰的藍色球和黃色球合成一個花球,這種合成包括2種方法,則此時出現的情況數為2 * 5! / (2! * 2! )。

 

  問題十三:15張卡片上分別寫着1,2,……15.現從中至少選出一張卡片。問:有多少種選擇方式,使得所選的所有卡片上的數均大於或等於被選的卡片的張數?

 

  我們從更加一般的角度來分析這個問題,如果給出了k張卡片,考慮用遞推的思維來建立起前后狀態的關系,我們進行如下的分類討論,記f[k]表示:

  (1)不選第k張卡片,則得到f[k-1]種情況。

  (2)選擇了第k張卡片。

    (i)不選其余卡片,1種情況。

    (ii)選擇其余卡片,很容易看到,當前情況就不能夠選第1張卡片了,對於剩余的2、3……k-1張,我們將其重新編號:1,2……k-2,可以看到,我們在f[k-2]種情況的基礎上,加上第k張卡片,由於增加了一張卡片數,應該將每個卡片的編號+1,這其實剛好與我們進行編號前的所有情況是一一對應的,由此我們得到了f[n-2]種情況。

  綜合起來,我們發現,f[n] = f[n-1] + f[n-2] + 1,基於這個遞推式,我們就很容易進行求解了。

 

  問題十四:設正整數m、n互素,s是任意一個整數,求集合{1,2,3……m+n-1}的子集A的數目,使得|A| = m,且∑x ≡ s(mod n),其中x∈A.

 

  我們容易看到,元素個數為m的子集個數為C(m+n-1,m)種,現在我們需要探討在這些種集合中,(∑x) mod n的結果是否會出現重復,或者說如何進行重復。

  我們基於這樣一條數論中結論,如果(p,n) = 1,則n mod p 、2n mod p…… (p-1)n mod p是1、2、……p-1的一個某種次序的排列(證明費馬小定理的用到的結論,可用鴿巢原理去證明)。那么現在用到這里,對於某種情況∑xk,我們進行xk <- xk + 1的操作,則∑xk增加了m,那么進行n次這樣的操作,結果便是0、1、2……n-1的一個某種次序的排列,那么基於這個結論,我們這個過程放在一個m + n - 1的數環上,對於某個長度為m的集合,旋轉n次,∑xk mod n = 1、2、3……n-1,可以看到,所有的情況都會存在與這樣一個旋轉的系中,因此我們得到結論,∑x ≡ s(mod n),s取0、1、2……n-1的情況數目是相等的,因此該題最終的答案是1/n * C(m+n-1,m).

 

  問題十五(uva 12034):

  A、B兩個人賽馬,最終名次有3種可能:並列第一;A第一B第二;A第二B第一。那么按照這種排名方法,給出整數n,求解最終排名的所有情況數。

分析:考慮基本的遞推原理就能夠得到這道問題的解,設dp[n]是n個人不同排名的情況數,選擇i個人成為並列第1,完成問題規模的削減,然后將各個情況累加起來,可以得到如下的狀態轉移方程:

 

 

#include<cstdio>
using namespace std;

const int MOD = 10056;
const int maxn = 1005;
int dp[maxn];
int C[maxn][maxn];

void init()
{
    for(int i = 0;i <  maxn;i++)
    {
         C[i][0] = 1;
         for(int j = 1;j <= i;j++)
               C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD;
    }
}
int main()
{
     init();


    dp[0] = 1;
    dp[1] = 1;
    dp[2] = 3;
    int n;
    int T;
    int tt = 1;


    for(int i = 3;i <= maxn;i++)
        {
             int sum = 0;
        for(int j = 1;j <= i;j++)
            {
                   sum += (C[i][j]*dp[i-j])%MOD;
                   //printf("%d\n",(C[i][j]*dp[i-j]));

            }
            dp[i] = sum%MOD;
        }

    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
          printf("Case %d: ",tt++);
        if(n == 1  || n == 2)  {printf("%d\n",dp[n]);continue;}

          printf("%d\n",dp[n]);



    }

}

 

 

  問題十六(uva 10213):

  有多少土地:

  在一個橢圓的土地當中,在邊界上有n個點,現在連接任意的兩個點,那么請問這塊橢圓土地能夠最多被分割成多少塊?

  分析:一道歐拉定理即視感非常強的題目,我們依然要借助節點v、邊的個數e來確定面數f,在這里要去掉最外面那個面,即在這道具體的題目當中,f = e-v+1.

  那么下面的問題還是求v、e。其實這里能夠看到已經和計算幾何不沾什么邊了,本質上來講是一個組合計數問題。

                         

 

  點:

  我們固定一條線段的起始點,然后遍歷剩余點作為終點,狀態參量i表示該線段左邊的點的個數,這是一個子問題,我們遍歷起始點,然后進行去重(充分理解這個分割狀態的過程,能夠看到每個點會被計算4次)就可以了。

   

  邊:

  基於求點數的窮舉計數方法,這里計算邊會變得非常簡單。

   

  但是很遺憾這個問題到這里還沒有結束,由於這道題目是多組數據,而且在時間復雜度上卡的很近,因此我們需要繼續的化簡。

 

  對於一個樣例,給出n,我們給出的公式是O(n)算法(循環計算),下面嘗試將其優化成O(1)。

 

 

  參考代碼如下:

 

 import java.math.BigInteger;
import java.util.Scanner;

public class main {
    public static void main(String[] args) {
        int t;
        Scanner in = new Scanner(System.in);
        t = in.nextInt();
        for(int Case =1 ;Case <= t;Case++)
        {
        
        
        
        BigInteger n = in.nextBigInteger();
        BigInteger a = n.pow(4);
        BigInteger b = n.pow(3).multiply(BigInteger.valueOf(6));
        BigInteger c = n.pow(2).multiply(BigInteger.valueOf(23));
        BigInteger d = n.multiply(BigInteger.valueOf(18));
        
        BigInteger ans = BigInteger.valueOf(0);
        
        ans = ans.add(a).subtract(b).add(c).subtract(d);
        ans = ans.divide(BigInteger.valueOf(24));
        ans = ans.add(BigInteger.valueOf(1));
        System.out.println(ans);

        
           
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM