POJ 3156 Interconnect(期望DP)

本题中图的具体形式是没有意义的,状态应该是联通块数量和大小,用数组记录这种状态。

比如(3,3,2)表示3个联通块,分别有3,3,2个点,它可以转移为(6,2)和(5,3)。很好理解转移到(6,2)的几率为3*3/(8×7/2),即:可改变状态的边和总边数之比,这样状态到状态间的概率就可求了。

期望DP要逆着推。设状态a可以到x,y,z,假设x,y,z到全联通的期望步数已求,为dp[x,y,z],转移的概率为pa,px,py,pz.

那么有等式  dp[a]=1+px*dp[x]+py*dp[y]+pz*dp[z]+pa*dp[a]。 移项得(1-pa)dp[a]=1+px*dp[x]+py*dp[y]+pz*dp[z]。dp[a]就可求了。显然这种形式要记忆化搜索。


记忆化要表示状态,用一个map <struct ,int > 来做记录。 struct内是一个int[30]的数组,要这样必须重载struct的小于号。

方法是在外面加:

bool operator < (const node &x,const node &y){
 for(int i=0;i<30;i++){
  if(x.a[i]<y.a[i]) return 1;
  if(x.a[i]>y.a[i]) return 0;
 }
 return 0;
}


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#include <algorithm>
#define mod 100007
#define eps 1e-7
#include <map>
int N,M;
int tot;
int fa[40];
int num[40];
double dp[100007];
int a[100007][40];
int cnt;
struct node{
	int a[30];
	node(){}
	node(int n[]){
		for(int i=0;i<30;i++){
			a[i]=n[i];
		}
	}
};

bool operator < (const node &x,const node &y){
	for(int i=0;i<30;i++){
		if(x.a[i]<y.a[i]) return 1;
		if(x.a[i]>y.a[i]) return 0;
	}
	return 0;
}

map <node,int> Hash;

bool cmp(int a,int b) {return a>b;}

int Find(int n){
	if(n==fa[n]) return n;
	return fa[n]=Find(fa[n]);
}

void Union(int x,int y){
	if(Find(x)==Find(y)) return ;
	fa[Find(x)]=Find(y);
}



double solve(int n){
	if(dp[n]>-0.5) return dp[n];
	if(a[n][1]==0) return dp[n]=0;
	dp[n]=1;
	int crl=0;
	for(int i=0;i<30;i++){
		if(!a[n][i]) break;
		crl+=a[n][i]*(a[n][i]-1);
	}
	crl/=2;
	double kk=1-crl*1.0/tot;
	for(int i=0;i<30;i++){
		if(!a[n][i]) break;
		for(int j=i+1;j<30;j++){
			if(!a[n][j]) break;
			int tmp[30];
			memset(tmp,0,sizeof(tmp));
			for(int k=0;k<j;k++){
				tmp[k]=a[n][k];
		    }
		    tmp[i]+=a[n][j];
		    for(int k=j;k<29;k++){
				tmp[k]=a[n][k+1];
		    }
		    tmp[29]=0;
		    sort(tmp,tmp+30,cmp);
		    node cur(tmp);

		    if(!Hash.count(cur)){
				Hash[cur]=++cnt;
				for(int k=0;k<30;k++) a[cnt][k]=tmp[k];
		    }
			dp[n]+=a[n][i]*a[n][j]*1.0/tot*solve(Hash[cur]);
		}
	}
	dp[n]/=kk;
	return dp[n];
}

int main(){
	while(~scanf("%d%d",&N,&M)){
		Hash.clear();
		cnt=0;
		memset(a,0,sizeof(a));
		memset(num,0,sizeof(num));
		for(int i=0;i<N;i++){
			fa[i]=i;
		}
		tot=N*(N-1)/2;
		for(int i=0;i<M;i++){
			int s,t;
			scanf("%d%d",&s,&t);
			Union(s-1,t-1);
		}
		for(int i=0;i<N;i++){
			num[Find(i)]++;
		}
		for(int i=0;i<mod;i++) dp[i]=-1;
		sort(num,num+30,cmp);
		node sta(num);
		Hash[sta]=++cnt;
		for(int i=0;i<30;i++){
			a[cnt][i]=num[i];
		}
		printf("%.7f\n",solve(1));
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值