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;
}

猜你喜欢

转载自blog.csdn.net/sincerit/article/details/83277672