题目链接:http://codeforces.com/problemset/problem/513/G2
题目大意:给定有n个数的一个数列,每次操作是等概率翻转一个区间,这样操作k次,求逆序数对个数的期望。
(1 ≤ n ≤ 30, 1 ≤ k ≤ 200)
思路:
f[k][i][j] 表示k次翻转以后a[i] 在a[j] 前面的概率,初始化条件是 f[0][i][j] = 1 f[0][j][i] = 0 ( i < j )
另外翻转区间 [i, j]的概率P = 1 / ( n * (n+1) / 2)
由于 翻转时[i, j]内元素位置都会交换,
所以枚举翻转时的要循环枚举区间内所有的元素的转移情况
(可以滚动……)
代码:
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
using namespace std;
const int N = 110;
int n, k;
double f[2][N][N];
double tmp[N][N];
int a[N];
void dp()
{
double p = 1.0 * n * (n + 1) / 2.0;
for (int tt = 1; tt <= k; tt++)
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[tt % 2][i][j] = 0;
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
for(int x = 1; x <= n; x++)
for(int y = x; y <= n; y++)
{
int a = i, b = j;
if(x <= i && i <= y) a = x + y - a;
if(x <= j && j <= y) b = x + y - b;
if(a > b) swap(a, b);
if(x <= i && j <= y) f[tt % 2][a][b] += (1.0 - f[(tt - 1) % 2][i][j]) / p;
else f[tt % 2][a][b] += 1.0 * f[(tt - 1) % 2][i][j] / p;
}
}
}
int main()
{
while(scanf("%d%d",&n, &k) != EOF)
{
double re = 0;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
memset(f, 0, sizeof(f));
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
f[0][i][j] = 1.0;
dp();
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
{
if(a[i] > a[j]) re += f[k % 2][i][j];
else re += 1.0 - f[k % 2][i][j];
}
printf("%.15f\n", re);
}
return 0;
}