HDU 2068 RPG的错排(错排公式 + 详解)

时间:2022-10-06 11:27:47

RPG的错排

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8996    Accepted Submission(s): 3699


Problem Description 今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁。RPG给他机会让他猜猜,第一次猜:R是公主,P是草儿,G是月野兔;第二次猜:R是草儿,P是月野兔,G是公主;第三次猜:R是草儿,P是公主,G是月野兔;......可怜的野骆驼第六次终于把RPG分清楚了。由于RPG的带动,做ACM的女生越来越多,我们的野骆驼想都知道她们,可现在有N多人,他要猜的次数可就多了,为了不为难野骆驼,女生们只要求他答对一半或以上就算过关,请问有多少组答案能使他顺利过关。  
Input 输入的数据里有多个case,每个case包括一个n,代表有几个女生,(n<=25), n = 0输入结束。  
Sample Input
1
2
0
 
Sample Output
1
1
此处运用了错排公式,错排公式详解如下:错排公式详解:
当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用M(n)表示,那么M(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
第二步,放编号为k的元素,这时有两种情况.1,把它放到位置n,那么,对于剩下的n-2个元素,就有M(n-2)种方法;2,不把它放到位置n,这时,对于这n-2个元素,有M(n-1)种方法;
综上得到
D(n) = (n - 1) * [D(n - 2) + D(n - 1)]
D(1) = 0,D(2) = 1
现在讲解大家疑惑的地方,也许大家对于那个D(n - 2)不疑惑,但是对于D(n - 1)的来由有点坑爹
现在进行讲解,当将n这个位置的元素放在了k这个位置,那么此时将k这个位置放在n的位置,当作是k本来不是在k的位置,而是在n的位置
也许大家有点听不懂,下面有图形说明:
HDU 2068 RPG的错排(错排公式 + 详解)
这幅图表示最开始正确的序列HDU 2068 RPG的错排(错排公式 + 详解)
这幅图表示第八个与第四个进行交换了
接着就是重中之重HDU 2068 RPG的错排(错排公式 + 详解)
所谓的d[i - 1] * (i - 1)的由来就是上面这幅图,将4这个数原本的位置不再看成是第一幅的位置,而是上面这幅图的位置,那么这个图的错排就是d[i - 1] * (i - 1)
另外一种容易理解的思路由于D[i - 1]中多加了一个元素f[i]那么这个元素可以放在D[i - 1]中错排序列中任意个,所以有(i - 1) * D[i - 1]中,然后是如果i - 1个数中有一个没有进行错排,那么这个数与f[i]进行交换,接着剩下的所有元素进行错排有(i - 1) * D[i - 2]个如此答案为D(n) = (n - 1) * [D(n - 2) + D(n - 1)]HDU 2068 RPG的错排(错排公式 + 详解)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL ans[20];
int n;
LL getsd(LL n,LL x) {
LL ret = 1;
for(int i = 0; i < x; i ++) {
ret = ret * (n - i) /(i + 1);
}
return ret;
}
int main() {
ans[0] = 1;
ans[1] = 0;
ans[2] = 1;
for(int i = 3; i <= 15 ; i ++) {
ans[i] = (ans[i - 1] + ans[i - 2]) * (i - 1);
}
while(~ scanf("%d", &n), n) {
LL ret = ans[0];
int m = n >> 1;
for(int i = 1; i <= m; i ++) {
ret += ans[i] * getsd(n,i);
}
printf("%I64d\n",ret);
}
return 0;
}