时间限制:
6000ms
单点时限:
1000ms
内存限制:
256MB
-
4 5 1 2 5 4 3 1 5 2 2 5 4 3 1 5 3 2 5 4 3 1 5 4 2 5 4 3 1
样例输出
-
0.000000000 0.600000000 0.900000000 1.000000000
描述
一日,崔克茜来到小马镇表演魔法。
其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它。初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开。崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗?
输入
第一行一个整数 T (T ≤ 100)表示数据组数。 对于每组数据,第一行有两个整数 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n)。 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 ai 个盒子的钥匙。
输出
对于每组询问,输出一行表示对应的答案。要求相对误差不超过四位小数。
对于某个箱子集合,若打开其中任意一个箱子都能依次使用箱子中的钥匙打开其他所有箱子,暂且把这样的箱子集合称作环。
比如在测试案例1中,(1,2,5)是一个环,(3,4)也是一个环。显然,不论打开(1,2,5)中的哪个箱子,其它两个箱子也会被打开。
这题的要求是打开所有的箱子的概率。打开箱子的总方法数是C(n,k),所有本题的关键是求打开所有箱子的方法数。
除此之外还要统计有多少环,以及每个环中的元素个数。
动态规划:
dp[i][j]表示用j把钥匙打开前i个环箱子的方法数。
dp[0][0]=1
dp[i+1][j+l]+=dp[i][j]*C(count(i+1),l) count(i+1)表示第i+1个环中的箱子数量
即用j+l把钥匙打开i+1的方法数,即打开第i+1个环共用了l把钥匙。
在程序中count的计数是从0开始的,即count(i)实际表示的第i+1环中的箱子数量。
#include <iostream>
#include <iomanip>
#include <vector>
#include <math.h>
#include "string.h"
using namespace std;
int main()
{
int t;
cin >> t;
vector<vector<double>>c(301, vector<double>(301));
for (int i = 0; i <= 300; i++)
{
c[i][0] = c[i][i] = 1.0;
for (int j = 1; j < i; j++)
c[i][j] = c[i - 1][j - 1] + c[i-1][j];
}
while (t--)
{
int n, k;
cin >> n >> k;
int *box;
int *visit;
box = new int[n + 1];
visit = new int[n + 1];
for (int i = 1; i <= n; i++)
{
cin >> box[i];
visit[i] = 0;
}
vector<int> count;
for (int i = 1; i <= n; i++)
{
int sum = 0;
int tmp = i;
while (visit[tmp] == 0)
{
visit[tmp] = 1;
tmp = box[tmp];
sum++;
}
if (sum!=0)
count.push_back(sum);
}
int ring_num = count.size();
if (ring_num > k)
{
cout << "0.000000000" << endl;
continue;
}
vector<vector<double>>dp(301, vector<double>(301));
dp[0][0] = 1.0;
for (int i = 0; i < ring_num; i++)
{
for (int j = 0; j < k; j++)
{
if (fabs(dp[i][j]) > 0.000001)
{
for (int l = 1; l <= count[i]&&j+l<=k; l++)
dp[i + 1][j + l] += dp[i][j] * c[count[i]][l];
}
}
}
printf("%.9f\n", dp[ring_num][k] / c[n][k]);
//cout << setprecision(9) << dp[ring_num][k] / c[n][k] << endl;
}
system("pause");
return 0;
}
真是哔了狗了,为什么输出不能用steprecision!!!!