本题中图的具体形式是没有意义的,状态应该是联通块数量和大小,用数组记录这种状态。
比如(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;
}