传球游戏【递推】

传球游戏【递推】

题目描述

上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。

游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大家表演一个节目。

聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有三个同学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共一行,有一个整数,表示符合题意的方法数。

输入输出样例

输入样例#1: 复制 3 3 输出样例#1: 复制 2

分析:这道题我想大家对于状态转移方程可以很容易想出来了,设f【x】【y】表示传y次传到第x个人(特别地,由于是圆圈所以这里的第n个人设置为0号,方便%)的方法,那么f【x】【y】=f【(x-1+n)%n】【y-1】+f【(x+1+n)%n】【y-1】(这个方程就不用解释了吧,很容易理解的)(这里加个n是防止x-1变为负数)。想出来了之后,怎么去推这个f【(x-1+n)%n】【y-1】+f【(x+1+n)%n】【y-1】呢?我们要推这个,就要想:f【(x-1+n)%n】【y-1】=f【(x-2+n)%n】【y-2】+f【(x+n)%n】【y-2】。那么怎么去推这个f【(x-2+n)%n】【y-2】+f【(x+n)%n】【y-2】?…最后,我们发现,这跟递归非常想,立马联想到记忆化搜索,看了一下数据范围:只要程序不错,这道题完全行得通!

#include

using namespace std;

int f[31][31];

int n,m;

inline int read()//读入优化,不会的读者可以直接用scanf

{

int f=1,x=0;

char s=getchar();

while(s<'0'||s>'9')

{

if(s=='-')

{

f=-f;

}

s=getchar();

}

while(s>='0'&&s<='9')

{

x=x*10+s-48;

s=getchar();

}

return x*f;

}

int dfs(int x,int y)

{

if(y==1&&(x==0||x==2))//从1号开始传起,传一次只能传给0号和2号

{

return 1;//只有一种方法

}

else if(y==1)//如果不是 0号和2号 且传球次数只有1次了,那么肯定行不通了,返回0

{

return 0;

}

else if(f[x][y]!=-1)//如果这个结果已经被搜索过了,直接返回

{

return f[x][y];

}

else

{

f[x][y]=dfs((x-1+n)%n,y-1)+dfs((x+1+n)%n,y-1);//没有的话进行递归

return f[x][y];//返回

}

}

int main()

{

n=read(),m=read();

memset(f,-1,sizeof(f));//先全部初始化为-1,因为f【x】【y】的值可能为0

printf("%d",dfs(1,m));//开始递归

return 0;

}

另外,这道题如果不用记忆华搜索直接递归会不会超时,我也没试过,有兴趣的读者可以自己尝试一下,谢谢各位

相关作品