1. 程式人生 > >sincerit 演算法競賽寶典 傳球遊戲

sincerit 演算法競賽寶典 傳球遊戲

傳球遊戲 題目描述
上體育課的時候,小蠻的老師經常帶著同學們一起做遊戲。這次,老師帶著同學們一起做傳球遊戲。

遊戲規則是這樣的:n個同學站成一個圓圈,其中的一個同學手裡拿著一個球,當老師吹哨子時開始傳球,每個同學可以把球傳給自己左右的兩個同學中的一個(左右任意),當老師再次吹哨子時,傳球停止,此時,拿著球沒傳出去的那個同學就是敗者,要給大家表演一個節目。

聰明的小蠻提出一個有趣的問題:有多少種不同的傳球方法可以使得從小蠻手裡開始傳的球,傳了m次以後,又回到小蠻手裡。兩種傳球的方法被視作不同的方法,當且僅當這兩種方法中,接到球的同學按接球順序組成的序列是不同的。比如有3個同學1號、2號、3號,並假設小蠻為1號,球傳了3次回到小蠻手裡的方式有1->2->3->1和1->3->2->1,共2種。

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

輸出
輸出檔案ball.out共一行,有一個整數,表示符合題意的方法數。

樣例輸入
3 3

樣例輸出
2

遞迴模擬搜尋(沒有遞推式子)只有到最後一步才知道是否找到一種解
所以用一個變數在最後一步來記錄方法數
如果有遞推式子的就不能用變數去記錄了,設定返回值就可以
比如放蘋果 solve(m,n) = solve(m-n, n) + solve(m, n-1) 只有兩部分的值加起來才能得到答案所以是有返回值的答案(個人對放蘋果和傳球遊戲的解答過程不一樣的理解)
這個解法是從模擬過程出發考慮的

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
int N, M, sum;
void game(int n, int m) {
  if (m == 0) {
    if (n == 1) ++sum;
    return;
  }
  if (n == 1) {
    game(n+1, m-1);
    game(N, m-1);
  }
  if (n == N) {
    game
(1, m-1); game(n-1, m-1); } if (n > 1 && n < N) { game(n+1, m-1); game(n-1, m-1); } } int main() { while (cin >> N >> M) { sum = 0; game(1, M); // 規定了從1開始傳 cout << sum << "\n"; } return 0; }

使用動態規劃—從最後要得到的結果出發推到過程
我們發現要球傳到某個人, 只能從左邊傳過來或者從右邊傳過來,設當前球的位置用i表示,傳球的次數用j表示,那麼dp[i][j]的方案數是左右兩邊的方案數之和
即 dp[i][j] = dp[i-1][j-1] + dp[i+1][j-1]
邊界考慮:
dp[1][j] = dp[n][j-1] + dp[2][j-1]
dp[n][j] = dp[n-1][j-1] + dp[1][j-1]
初始化 dp[1][0] = 1
dp[1][0] = 1這個初始化可以參考遞迴方程的出口的含義
表示當又傳回到1號同學時並且傳球次數用完了就找到了一種傳球方法
例如: dp[2][1] = dp[1][0] + dp[3][0] = 1 + 0 = 1;
也可以看成把2位置上的求給1,3號位置的人並且次數用完一次
dp[i][j] // i號位置上的球,傳球次數剩j次的方案數
我們要求的是dp[1][m]傳球方案數

#include <stdio.h>
#include <cstring>
int dp[100][100]; 
// dp[i][j]當前球號在位置i,傳球次數為j時的方案數
// 轉移方程 dp[i][j] = dp[i-1][j-1] + dp[i+1][j-1];
// 邊界條件 dp[1][0] = 1; 
int main() {
  int n, m; // n個人傳m次 
  while (~scanf("%d%d", &n, &m)) {
    memset(dp, 0, sizeof(dp));
    int i, j;
    dp[1][0] = 1; // 從1號位置開始傳球
    for (i = 1; i <= m; i++) {
      for (j = 2; j < n; j++)
        dp[j][i] = dp[j-1][i-1] + dp[j-1][i-1];
      dp[1][i] = dp[n][i-1] + dp[2][i-1];
      dp[n][i] = dp[n-1][i-1] + dp[1][i-1];
    }
    printf("%d\n", dp[1][m]);
  }  
  return 0;
}