您的位置:首页 > 其它

随机图 概率+组合数学

2017-11-01 21:13 387 查看
【问题描述】

  随机生成一张n个点的无向图。把它的顶点标号为1~n。

  对于一个点对i,j(1 <= i < j <= n),有千分之p的概率成为这张图中的一条边(i,j)。不同的边出现的概率是相互独立的。

  求产生一个至少含有一个大于等于4的连通块的图的概率是多少。

【输入格式】

  一行两个整数n,p,表示图的点数和边在图上的概率。

【输出格式】

  输出一行一个实数,表示所求的概率。结果保留4位小数。

【输入样例】

6 100

【输出样例】

0.1184

【样例解释】

时间限制:1秒 内存限制:128M

【数据范围】

4<=n<=100

——————————————————————————————————————————————————————

实际上好像有直接dp的做法,这里我就直接给出我自己的做法了(感觉加深了对组合数学的理解?雾)。

用补集的思想。P(图中至少包含一个以上的4个点的连通分量)= 1-P(图中连通分量大小最大的分量大小不超过3)

可以考虑枚举图中三种连通分量的个数(设为n1,n2,n3)

设g(n1,n2,n3)为图中含有n1个大小为1的连通分量(x1),n2个……(x2),n3个……(x3)的概率。

则ans = 1-sum{ g(n1,n2,n3) | n1+n2 * 2+n3 * 3=N }

P=P/1000

有C(N,n1)种方法选出n1所在的位置,然后有C(N-n1,n2*2)种方法选出n2所在的位置,最后n3的位置就是确定的了。

所以对于确定的n1,n2,n3,一共有C(N,n1) * C(N-n1,n2*2)种选法选出三种连通分量各自所在的位置。

这些方法都是独立的,相加,但是三个分量的形成是同时发生的,所以三种分量各自在对应大小的区间形成的概率应该相乘。

关键问题:怎么计算在给定数量的位置中全都形成大小为1/2/3的连通分量的概率。

图中有n个点的时候:

某一个点自己形成一个大小为1的连通分量x1的概率P1(n)=(1-P)^(n-1)(跟这个点有关的边有n-1条,所有的都没有出现)

某两个点一起形成一个大小为2的连通分量x2的概率P2(n)=P*(1-P)^(2n-4)(跟这两个点有关的边有2N-3条,其中只有连接这两个点的出现了,其他的都没出现)

某三个点一起形成一个大小为3的连通分量x3的概率P3(n)=3 * P * P * (1-P)^(3n-8)+P * P * P * (1-P)^(3n-9)

(跟这三个点有关的边有3N-6条,可能只需要两条边三个点就连在一起了,这个时候出现2条,三种方式,有3N-8条没有出现;还有可能有三条边把这三个点连接在一起)

但是假如现在有k个点,要计算这k个点全部都是大小为1的连通分量的概率的话P1^k这个答案是不正确的,重复计算了每一条边的概率贡献。

应该递归计算。

设f1(k)表示在k个点中只存在x1的概率,f1(k)=C(k-1,0) * P1(k) * f1(k-1)

设f2(k)表示在k个点中只存在x2的概率,f2(k)=C(k-1,1) * P2(k) * f2(k-2)

设f3(k)表示在k个点中只存在x3的概率,f3(k)=C(k-1,2) * P3(k) * f3(k-3)

这样递归计算的话每条边对概率的贡献就不会重复计算了。

对于每一种n1,n2,n3,内部是独立的可以用递推来计算,但是这三个板块之间的边的概率还没有考虑到(当然全部都没有出现)。

一共有n1 * (n2 * 2+n3 * 3)+n2 * n3 * 6条跨越板块的边没有出现,这种情况的概率P4=(1-P)^(n1 * (n2 * 2+n3 * 3)+n2 * n3 * 6)

所以说对于一种n1,n2,n3的方案的概率就出来了:

g(n1,n2,n3)=C(N,n1) * C(N-n1,n2 * 2) * P4 * f1(n1) * f2(n2 * 2) * f3(n3 * 3)

然而组合数太大了,方便起见直接取了阶乘的自然对数来算,最后算答案的时候乘方算回来就可以了。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=105;

int N;
double P;
long double C[maxn][maxn],ln[maxn];
long double f1[maxn],f2[maxn],f3[maxn],P1[maxn],P2[maxn],P3[maxn];
double ans;

void get_C()
{
ln[0]=log(1);
for(int i=1;i<=N;i++) ln[i]=ln[i-1]+log(i);
for(int i=0;i<=N;i++)
{
C[i][0]=log(1);
for(int j=1;j<=i;j++)
C[i][j]=ln[i]-ln[i-j]-ln[j];
}
}
long double qkpow(long double x,int y) { return log(x)*y; }
void ready()
{
f1[0]=f2[0]=f3[0]=log(1);
for(int k=1;k<=N;k++)
{
P1[k]=qkpow(1-P,k-1);
f1[k]=C[k-1][0]+P1[k]+f1[k-1];
}
for(int k=2;k<=N;k+=2)
{
P2[k]=log(P)+qkpow(1-P,2*k-4);
f2[k]=C[k-1][1]+P2[k]+f2[k-2];
}
for(int k=3;k<=N;k+=3)
{
P3[k]=log(3*P*P*exp(qkpow(1-P,3*k-8))+P*P*P*exp(qkpow(1-P,3*k-9)));
f3[k]=C[k-1][2]+P3[k]+f3[k-3];
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);

scanf("%d%lf",&N,&P);
P/=1000;
get_C();
ready();

long double P4=0;
for(int n1=0;n1<=N;n1++)
for(int n2=0;n2*2<=N;n2++)
{
if(N-n1-n2*2<0 || (N-n1-n2*2)%3!=0) continue;
int n3=(N-n1-n2*2)/3;
P4=qkpow(1-P,n1*(n2*2+n3*3)+n2*n3*6);
ans+=exp(C
[n1]+C[N-n1][n2*2]+P4+f1[n1]+f2[n2*2]+f3[n3*3]);
}

printf("%.4lf",1-ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: