【动态规划】随机图

题目描述

   随机生成一张n个点的无向图。把他顶点标号为1~n。对于一个点对i,j(1 <= i < j <= n),他有千分之p的概率成为这张图中的一条边(i,j)。不同的边出现的概率是相互独立的。那么生一个至少含有一个大于等于4的连通块的图的概率是多少?

分析

贼难,不想分析

解法一:正难则反,对症下药

正难则反
   存在一个大于等于4的连通块的概率,等价于:1减去所有连通块都小于等于3的概率。 
   状态函数:
   f(i,j,k)表示i个点的图中,有大小为2的连通块j个,大小为3的连通块k个,大小为1的联通块有i-2*j-3*k个的概率。
   边界:
   f(0,0,0)=1;
   转移方程:
   刷表法——分析在f(i,j,k)的基础上新加入1个点的状态转移:
   情况1、若新点与前面i个点中任何一个都不连边,则状态转移成 f(i+1,j,k)+=f(i,j,k)*(1-p)^i
   情况2、若新点与前面i个点中任意一个大小为1的连通块 连一条边构成一个大小为2的连同块,则状态转移成:f(i+1,j+1,k)+=f(i,j,k)*(i-2*j-3*k)*p*(1-p)^(i-1)
   情况3、若新点与前面i个点中任意 两个 大小为1的连通块连一条边构成一个大小为3的连同块,则状态转移成:f(i+1,j,k+1)+=f(i,j,k)*C(i-2*j-3*k,2)*p*p*(1-p)^(i-2)
   情况4、若新点与前面i个点中任意一个大小为2的连通块连一条边构成一个大小为3的连同块,则状态转移成:f(i+1,j-1,k+1)+=f(i,j,k)*2*j*p*(1-p)^(i-1)
   情况5、若新点与前面i个点中任意一个大小为2的连通块连两条边构成一个大小为3的连同块(大小为2的连通块的两个点分别连一条边),则状态转移成:f(i+1,j-1,k+1)+=f(i,j,k)*j*p*(1-p)^(i-1)

   对不起我抄题解了

解法二:正难则反+大佬就是不一样——%%%通解通法

借鉴了某位超级大犇的算法,强的一p。

f(i)表示i个结点能够连通的概率

g(i)表示i个结点无法满足至少有一个连通块大于等于4的概率

正难则反:先考虑加入结点不能使图连通。对于新加入的结点,分析:

若它和已有的i-1个结点形成了一块大小为j的连通块,相当于从i-1个结点中选择j-1个出来和现在的这个结点形成连通块,那么形成的概率为f(j)*C(i-1,j-1) 

要使这个连通块不连通,则剩下的i-j个结点和这个连通块里面的所有节点都不能连通,概率为((1-p)^(i-j))^j=(1-p)^(j*(i-j))

所以说此时形成大小为j的连通块并且这i个结点不连通的概率:f(j)*C(i-1,j-1)*(1-p)^(j*(i-j)) 

g(i)类似,在这j个结点不与外界连通的同时,剩下的i-j个结点的大小也不能大于等于4,所以概率为:f(j)*C(i-1,j-1)*(1-p)^(j*(i-j))*g(i-j)

综上,f(i)=1-sum{f(j)*C(i-1,j-1)*(1-p)^(j*(i-j)) | 1<=j<i}

g(i)=sum{f(j)*C(i-1,j-1)*(1-p)^(j*(i-j))*g(i-j) | 1<=j<=3}

当然,i<=3时,就算这i个结点全部连通,都无法满足至少有一个连通块大于等于4,所以说此时g(i)+=f(i)

但是当j*(i-j)很大的时候,(1-p)^(j*(i-j))会趋近于0,题目精度要求不高,取对数避免

最终答案ans=1-g(n) 

贴一下算法二的代码

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int maxn=105,maxm=10005;
int n;
double p;
void workin(){
	scanf("%d%lf",&n,&p);
	p/=1000;
	return;
}
double C[maxn][maxn],fact[maxn],epow[maxm];
void ready(){
	epow[0]=0;
	for(int i=1;i<=10000;i++){
		epow[i]=epow[i-1]+log(1-p);
	}
	fact[0]=fact[1]=0;
	for(int i=2;i<=n;i++){
		fact[i]=fact[i-1]+log(i);
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i;j++){
			C[i][j]=fact[i]-fact[j]-fact[i-j];
		}
	}
	return;
}
double d[maxn],f[maxn];
void dp(){
	d[1]=f[1]=0;
	for(int i=2;i<=n;i++){
		double sum=0;
		for(int j=1;j<i;j++){
			sum+=exp(d[j]+C[i-1][j-1]+epow[j*(i-j)]);
		}
		d[i]=log(1-sum);
		sum=0;
		for(int j=1;j<i && j<=3;j++){
			sum+=exp(d[j]+C[i-1][j-1]+epow[j*(i-j)]+f[i-j]);
		}
		f[i]=log(sum);
		if(i<4){
			f[i]=log(exp(f[i])+exp(d[i]));
		}
	}
	return;
}
void solve(){
	ready();
	dp();
	printf("%.4lf",1-exp(f[n]));
	return;
}
int main(){
	freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	workin();
	solve();
	return 0;
}
最后说一句,fuse123fuseTQL!!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值