POJ 3156 Interconnect 并查集+期望dp

题意:

有n个城市,m条边。现在每一年随机选两个不同的城市,然后建一条边。问多少年后所有城市连通?输出期望值。


解:

先用并查集处理出有多少个连通块。
状态的表示是每个连通块中点的数目。
如果某一年选的两个点属于同一连通块,就是转移到了自己。
否则就是转移到了另一个状态。移项解方程即可。
就是状态表示用了哈希表,略微复杂点。

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s)  memset(a,x,(s)*sizeof a[0])
#define mem(a,x)  memset(a,x,sizeof a)
#define ysk(x)  (1<<(x))
#define sqr(x)  ((x)*(x))
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=30    ;
const int mod=19993    ;
const int N=500000;
int n,m,x,y;
int pre[maxn+3],cnt[maxn+3];
int find(int x)  { return x==pre[x]?x:pre[x]=find(pre[x]); }
bool cmp(int a,int b)  {return a>b;}

struct Code
{
   int a[32],s;
   Code()
   {
      mes(a,0,n);
      s=0;
   }
   void clear()
   {
       mes(a,0,n);
       s=0;
   }
   int normalize()
   {
       sort(a,a+n,cmp);
       s=0;
       for(int i=0;i<n;i++)
       {
           if(!a[i])  break;
           s=s*31+a[i];
           s%=mod;
       }
       return s;
   }
   bool operator==(const Code & y)const
   {
       for0(i,n)
       {
           if(!a[i])  break;
           if(a[i]!=y.a[i])  return false;
       }
       return true;
   }
   bool end()
   {
       return a[0]==n&&a[1]==0;
   }

};
typedef pair<Code, double> pcd;

struct Hsh
{
   vector<pcd>G[mod+3];
   void insert(Code & x,double& y)
   {
       int p=x.s;
       G[p].push_back(make_pair(x,y));
   }
   double find(Code x)
   {
       int p=x.s,siz=G[p].size();
       for0(i,siz)
       {
           if(G[p][i].first==x)  return G[p][i].second;
       }
       return -1;
   }
   void clear()
   {
       for0(i,mod)  G[i].clear();
   }


}hmp;



double DP(Code st)
{
    if(st.end())  return 0;
    double x=hmp.find(st);
    if(x>=-0.5)  return  x;
    int sum=0;double ans=0;
    int F=n*(n-1)/2;
    for0(i,n)
    {
        if(!st.a[i]) break;
        sum+=(st.a[i])*(st.a[i]-1)/2;
    }
    for0(i,n) for(int j=i+1;j<n;j++) if(st.a[i]&&st.a[j])
    {
        int ret=st.a[i]*st.a[j];
        Code t=st;
        t.a[i]=st.a[i]+st.a[j];
        t.a[j]=0;
        t.normalize();
        ans+=DP(t)*ret/F;
    }
    ans++;
    ans/=(1-   1.0*sum/F  );
    hmp.insert(st,ans);
    return ans;
}

void solve()
{
    hmp.clear();
    mes(cnt,0,n+1);
    for0(i,n)  cnt[find(i)]++;
    int tot=0;Code st;
    for0(i,n) if(cnt[i])
    {
        st.a[tot++]=cnt[i];
    }
    st.normalize();
    cout<<fixed<<setprecision(8)<<DP(st)<<endl;

}
int main()
{
   std::ios::sync_with_stdio(false);
   while(cin>>n>>m)
   {
       for0(i,n) pre[i]=i;
       for0(i,m)
       {
           cin>>x>>y;x--;y--;
           int tx=find(x),ty=find(y);
           if(tx!=ty)  pre[tx]=ty;
       }
       solve();
   }
   return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值