转自http://acm.hdu.edu.cn/showproblem.php?pid=4503
解题转自http://blog.csdn.net/geniusluzh/article/details/8751274
今天是一年一度的植树节,腾讯幼儿园要求每个老师在班里选出几个小朋友一起去野外种植小树苗,根据学校的整体安排,湫湫老师的班里要选出3个小朋友。 已知湫湫的班里共有n个孩子,每个孩子有Bi个朋友(i从1到n),且朋友关系是相互的,如果a小朋友和b小朋友是朋友,那么b小朋友和a小朋友也一定是好朋友。为了选择的公平性,湫湫老师会随机抽取3个小朋友出来(每个人被抽到的概率相同),但是她很希望这3个小朋友之间的关系完全相同,湫湫老师想请你帮她算算抽到的3个小朋友正好关系相同的概率是多少?
PS. 关系相同就是指要么3个人互相是好朋友,要么3个人互相都不是好朋友。
1 5 3 3 3 3 4
题意自不必多说,那我们来看看题中相同关系的定义:互相为朋友关系或者是彼此都没有朋友关系,也就是说三人之间有三条边或者是有零条边都是称为相同关系的。
已经给出了每个点的朋友的个数,也就是与该点相连的点的个数,我们设第i个点的朋友个数为p[i]。于是使用这样一种策略来选出三个点:首先我们选择i号节点,然后我们在p[i]个朋友中选一个;然后在剩余的人中选一个,也就是在n-1-p[i]的人中选一个人;这样子选的三个人有什么特点呢?
我们看到从朋友中选了一个,保证了相同关系中三人无边的情况不会出现;然而在非朋友中选了一个,保证了相同关系中三条边的情况不会出现。也就说上面选三个点的策略保证排除了所有的相同关系的情况,所以对所有i的p[i]*(n-1-p[i])求和就是相同关系补集的元素个数。
再者我们发现按上面的策略选的三个点,没种三点情况总会被重复选一次,于是需要把上面的结果除以2,显然这是没有问题的。
知道了这些,结果就很简单了,其实我认为代码可以不贴。
#include <iostream>
#include <cstdio>
using namespace std;
int C(int n, int m)
{
if(m > n)
return 0;
else if(m == n)
return 1;
else
{
int ans = 1;
for(int i=n-m+1; i<=n; i++)
ans *= i;
for(int i=1; i<=m; i++)
ans /= i;
return ans;
}
}
int main()
{
int T, n;
int a, b;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
a = b = 0;
int temp;
double ans = 0;
for(int i=0; i<n; i++)
{
scanf("%d", &temp);
ans += (double)temp*(n-1-temp);
}
ans /= 2.0;
ans = ans/(double)(C(n, 3));
printf("%.3lf\n", 1-ans);
}
return 0;
}