蒜头君的新游戏

工作空闲之余,蒜头君经常带着同事们做游戏,最近蒜头君发明了一个好玩的新游戏:n 位同事围成一个圈,同事 A 手里拿着一个兔妮妮的娃娃。蒜头君喊游戏开始,每位手里拿着娃娃的同事可以选择将娃娃传给左边或者右边的同学,当蒜头君喊游戏结束时,停止传娃娃。此时手里拿着娃娃的同事即是败者。

玩了几轮之后,蒜头君想到一个问题:有多少种不同的方法,使得从同事 A 开始传娃娃,传了 m次之后又回到了同事 A 手里。两种方法,如果接娃娃的同事不同,或者接娃娃的顺序不同均视为不同的方法。例如 1>2>3>1 和 1>3>2>1 是两种不同的方法。

输入格式

输入一行,输入两个整数n,m(3n30,1m30),表示一共有n 位同事一起游戏,一共传m 次娃娃。

输出格式

输出一行,输出一个整数,表示一共有多少种不同的传娃娃方法。

样例输入

3 3

样例输出

2

写这第一篇博客主要是因为在刚刚开始的学习动态规划的路上就撞了个满头包。

这道题理解了以后发现还是比较简单的,但是没有理解之前,特别是思路错误之后就会一步一步走进死胡同出不来。

首先写一下自己的错误思路:

把动态规划的阶段分为人数和次数,尝试去找(n, m)和(n - 1, m)以及(n, m - 1)的关系,甚至最后不死心去找和(n - 2,m - 2)的关系。错误的点是在于并没有把阶段分解成可推导的,而是武断的通过题目无脑分阶段:n个人,m次,那就建一个[n][m]的二维数组嘛,一个人传一次肯定是0,那就试着找出他们之间的关系就好了。然而如果分解成的各个阶段不能推导的话,那动态规划相比于枚举法就没什么优势了,甚至还做不出来。

正确的思路:

不要去想什么最后要的结果是什么,而是去分析这个过程,把n个人分别标号成1,2,3,4......n,如果最后的结果是要得到m次之后传到标号为1(即初始位置)的手里的话,那m - 1次肯定是在2号或者n号手中,这样状态转移方程就可以很简单写出来了:

f[i][j] = f[i - 1][j - 1] + f[i - 1][j + 1] 

*i表示的传递的次数,j表示的最后传递到的标号数,j号的左右两边的邻居分别是j + 1号和j - 1号。

但是要注意的是考虑到同事要围坐成环,即1号的邻居是2号和n号,n号的邻居是n - 1号和1号。

边界值就是传一次传到各个标号的方案数:f[1][2] = 1, f[1][n] = ,其余都是0,因为1次根本传不到。

通过1次的就可以通过状态转移方程推导出2次的情况,然后推导出3次直到m次。

Java代码如下:

public class ThrowBall {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt(), m = in.nextInt();
in.close();

int[][] f = new int[m + 1][n + 1];

f[1][2] = 1;
f[1][n] = 1;
//从第二次开始计算
for(int i = 2; i <= m; i++){  
            for(int j = 1; j <= n; j++){  
                if(j == 1)
                f[i][j] = f[i-1][2] + f[i-1][n];  
                else if(j == n)
                f[i][j] = f[i-1][1] + f[i-1][n-1];  
                else 
                f[i][j] = f[i-1][j-1] + f[i-1][j+1];  
            }  
        }
System.out.println(f[m][1]);
}
}


你可能感兴趣的:(蒜头君的新游戏)