传球问题
Time Limit:1000MS Memory Limit:30000KB
Description
排球课上,老师进行在共青场进行传球练习,一共有p人参加,一开始球在你的手里。每次传球时,持球者都会把球传给另外一个人。传了n次后,球又回到了你的手里。现在老师想知道有多少种传法,他说如果给出答案,会有奖励的。于是大家各自开动大脑,想办法解决。作为计算机系的你,想要写一个程序来解这个问题……
提示:
例如,对于p=4,n=4,有21种传法。设四人分别为甲、乙、丙、丁,你是甲。若第一次球传给乙,则有以下7种传法:甲乙甲乙甲,甲乙甲丙甲,甲乙甲丁甲,甲乙丙乙甲,甲乙丙丁甲,甲乙丁乙甲,甲乙丁丙甲。若第一次传给丙和丁,同理各有7种传法。共计21种。
Input
本题有多组测试数据。
每组数据一行,每行有两个整数p和n。其中,2<=p<=1000000,2<=n<=2^31-1。
当一行中的p和n都为0时,输入结束。
Output
每组数据输出一行,每行一个整数。因为数字可能会非常大,所以你只要输出总数除2005的余数即可。
Sample Input
4 4
4 10
1000 1000
0 0
Sample Output
21
728
1
这道题是一个组合数学问题,碰到这题我们的第一想法肯定是如果能找出递推公式,应该就能解决问题。可是递推公式不是很好找,还是要仔细分析下题目的。可以用个数组表格来模拟下这道题,如下图所示:
假设用f(n)表示传了n次的方案数,从题目中很容易看出,相邻的两个不能一样,因此第n-1次不能传到"你"手里。
我们来分析下,找到f(n)的递推公式,影响f(n)的有两种情况:
#1:若n-1位置和首位相同,则n-1位置定死了就一种选择,则f(n)=f(n-2)*(p-1)
#2: 若n-1位置和首位不相同,则n位置不能和首位相同,并且不能和n-1位置选择相同,则f(n)=f(n-1)*(p-2)
因此,综上所述,f(n) = f(n-2)*(p-1) + f(n-1)*(p-2)
但是题目给的p和n非常的大,而且也提示了让去2005的模,因此f(n) =(((p-1)%2005)*f(n-2) + ((p-2)%2005)*f(n-1)%2005即可。
而且基本上涉及到取模,肯定会有循环发生,因此我们没必要算出所有数,找到循环即可。不然数组f定义的过大,内存要超。
下面给出代码:
#include <iostream>
#include <cstring>
using namespace std;
const int m = 2005;
short f[3000];
int main()
{
int p,n;
while(cin >> p >> n)
{
if(p == 0 && n == 0)
{
break;
}
memset(f,0,sizeof(f));
f[1] = 0;
f[2] = (p-1)%m;
int i;
for(i = 3; i <= n; ++i)
{
f[i] = (((p-1)%m)*f[i-2] + ((p-2)%m)*f[i-1])%m;
if(f[i-1] == f[1] && f[i] == f[2])//产生循环即可跳出
{
break;
}
}
if(n > i)//循环
{
int c = i-2;//求得循环长度
int ans = n%c;
if(ans == 0)
{
ans = c;
}
cout << f[ans] << endl;
}
else//n比i小,直接输出
{
cout << f[n] << endl;
}
}
return 0;
}