1. 程式人生 > >演算法記錄:擊鼓傳花問題c語言實現

演算法記錄:擊鼓傳花問題c語言實現

題目描述

學校聯歡晚會的時候,為了使每一個同學都能參與進來,主持人常常會帶著同學們玩擊鼓傳花的遊戲。遊戲規則是這樣的:n個同學坐著圍成一個圓圈,指定一個同學手裡拿著一束花,主持人在旁邊背對著大家開始擊鼓,鼓聲開始之後拿著花的同學開始傳花,每個同學都可以把花傳給自己左右的兩個同學中的一個(左右任意),當主持人停止擊鼓時,傳花停止,此時,正拿著花沒傳出去的那個同學就要給大家表演一個節目。
聰明的小賽提出一個有趣的問題:有多少種不同的方法可以使得從小賽手裡開始傳的花,傳了m次以後,又回到小賽手裡。對於傳遞的方法當且僅當這兩種方法中,接到花的同學按接球順序組成的序列是不同的,才視作兩種傳花的方法不同。比如有3個同學1號、2號、3號,並假設小賽為1號,花傳了3次回到小賽手裡的方式有1->2->3->1和1->3->2->1,共2種。
要求:

 輸入

輸入共一行,有兩個用空格隔開的整數n,m(3<=n<=30,1<=m<=30)

 輸出

輸出共一行,有一個整數,表示符合題意的方法數

輸入用例:
3 3
輸出用例:
2

思路分析:

由於n人圍成的是一個迴圈體,並且每個人都只可以往相鄰的兩個人的方向傳遞花。也就是說,每個人都往下一個人傳時,都可以產生兩種情況。同時傳遞次數會減去其1。由此,假設我們設定起點位置的編號為0,那麼我們可以假設一個二維陣列dp來記錄每個編號人員能夠在多少次傳遞次數中傳遞到起點位置,其橫列表示次數,縱列表示當前人員的編號。而我們針對兩種情況可能出現序列個數的總和,則為我們的正解。也就是我們得出這麼一條方程:
dp[m][n] = dp[m-1][n-1] +dp[m-1][n+1]  
在這個方程上,其實就可以構成一種類遞迴結構。也是動態規劃結構的初始模型,在這個問題上,我們進行進一步的完善:
1.結束條件:
在這個問題上,並不是每種情況都會在次數用完之前回到原點,因此,我們需要對能夠回到原點的行為進行標識。也就是,當可用次數為1時,距離起點0附近的兩個節點(1 || n-1 )能夠立刻返回至起點中,此時也就是說
dp[1][1] = 1;
dp[1][n-1] = 1;
2.需要進行多少遍迴圈:
由於我們需要探究的是每一次傳遞都會有哪些操作,也就是說,我們需要確保每一次次數的消耗都需要和他的情況,也就是左右兩邊的人在m-1次次數下到達起始點的個數。由此我們既需要對每個編號人員及其次數進行遍歷。因為次數引導編號,所以次數是活躍迴圈。因此,我們定義兩個迴圈,外層迴圈遍歷人員編號,內層則遍歷次數。在這裡需要注意的一個問題是,迴圈問題,也就是當n-1之後的左邊人員會是0.因此需要進行處理。
綜述:可得出以下狀態轉移方程,以i代表當前次數,j代表當前人員編號:
dp[i][j] = dp[i-1][(n-j-1)%n]+dp[i-1][(n-i+1)%n];

得出狀態轉移方程和結束調節,我們便可以完善程式碼了:

#include<stdio.h>
int dptest(int n,int m);
int main(void){
    int m,n;
    scanf("%d %d",&n,&m);
    int result = dptest(n,m);
    printf("%d\n",result);
    return 0;
}

int dptest(int n,int m){
        int dp[31][31];
        dp[1][1] = 1;
        dp[1
][n - 1] = 1; for (int i = 2; i <= m; i++) { for (int j = 0; j < n; j++) { dp[i][j] = dp[i - 1][(n-j-1) % n] + dp[i - 1][(n-j+1) % n]; } } return dp[m][0]; }