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