UVA11637
UVA11637 - Garbage Remembering Exam
题意:
大概意思是,有n个单词,分别打乱放在一个环形的,一个非环形里面,环形的两个单词距离为顺时针逆时针的最小值,非环形的就是位置的差的绝对值,如果有一对单词,在两个里面的距离都是不大于k,那么这单词为无效单词,问平均会出现多少个无效单词
> 思路:
组合概率,假设在非环形形成了一个随机序列,那么我们给它标号1-n,如果我们能分别算出1-n的有效概率,那么就等于算出了无效概率,那么有效概率等于和它距离大于k的那些位置的所有单词拿出来,假设有x个,在环形序列中选一个位置放这个单词,然后它周围的2k的位置(因为是环形序列)要放入这些x个,就是x个选2k个放进去,再全排列,然后剩下n-2k-1个位置再全排列,然后总情况数为n的全排列,所以这样公式一组合起来就是nC(2k,x)2k!(n−2k−1)!/n!,化简一下公式得到x!(n−2k−1)!/(x−2k)!/(n−1)!,这个数值无法直接运算,然后观察能发现他的分子和分母可以合起来考虑变成乘除的项数相同,分子为(n−2k−1)∗(n−2k−2)∗...∗(x−2k+1),分母为(n−1)∗(n−2)∗...∗(x+1),那么就可以先递推预处理出一个p数组,p[x]就表示原公式解,那么整个答案为就是非环形序列每个位置的概率*1求出期望,然后累加求总和,最后再用n减去该值得到无效单词的期望。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
//转化计算式,变成多个x-i/n-i相乘(x指在非环中当前数在它k范围外数的个数),然后预处理
const int N = 100005;
int n, k;
double p[N];
void init(int n, int k) {
memset(p, 0, sizeof(p));
p[n - 1] = 1.0;
for (int i = n - 1; i > 2 * k; i--) {
p[i - 1] = p[i] * (i - 2 * k) * 1.0 / (i * 1.0) ;
}
}
double solve(int n, int k) {
init(n, k);
double ans = 0;
for (int i = 1; i <= n; i++) {
int x = max(i - k - 1, 0) + max(n - i - k, 0);
ans += p[x];
}
return n - ans;
}
int main() {
int cas = 0;
while (~scanf("%d%d", &n, &k) && n || k) {
printf("Case %d: %.4lf\n", ++cas, solve(n, k));
}
return 0;
}