UVA 11600 Masud Rana

来源:互联网 发布:php商城分销系统源码 编辑:程序博客网 时间:2024/04/30 12:14

题目大意:有n城市,两两之间有一条道路,某些道路上有妖怪,某些没有,有一个人他第一天晚上在1号城市,白天随机选择一个新城市,走过一条道路,把这条道路上的妖怪消灭掉,然后晚上住在这个城市,在平均情况下,需要多少个白天才能让任意两个城市之间均可以不经过有妖怪的道路而相互到达。

思路:一看n范围,就应该知道是状态压缩。。先dfs把所有连通分量缩点,设d[ u ][ s ] 表示当前在u,经过了s状态的点的期望,先考虑把s这些点搞定需要多少个白天,d[ u ][s ] = (num - 1)/(n - 1)*d[ u ][ s ],则d[ u ][ s ] = (n - 1)/(n - num),num为s里的所有点数,然后考虑那些没有到达的点,d[ u ][ s ] += SIGMA( d[ k ][ s ^ (1<<k) ]*cnt[ k ]/( n - num ) ),cnt[ k ] 为k这个分量里包含的点数。这里n = 30 比较大,数组开不下,可以开map,记忆化。然后这里还有一个坑,那就不是保留一位小数,而是6位。。

表示概率论学的不好,样例都算不出来,看着别人代码才知道这个概率怎么算的。。 = =

代码如下:

#include<cstdio>#include<cstring>#include<vector>#include<map>#include<algorithm>using namespace std;map <int,double> d[33];const int MAXN = 33;int n;vector <int> G[MAXN];int cnt[MAXN];int vis[MAXN];int dfs(int u){    vis[u] = 1;    int ans = 1;    for(int i = 0;i<G[u].size();i++)    {        int v = G[u][i];        if(!vis[v])            ans += dfs(v);    }    return ans;}int tot;int count(int s){    int c = 0;    for(int i = 0;i<=tot;i++)        if(s&(1<<i))        {            c += cnt[i];        }    return c;}double dp(int u,int s){    if(d[u].count(s)) return d[u][s];    double &ans = d[u][s];    int cur = count(s);    if(cur == n) return 0;    ans = 1.0*(n-1)/(n-cur);    for(int i = 0;i<=tot;i++)        if(!(s&(1<<i)))        {            ans += dp(i,s|(1<<i))*cnt[i]/(n-cur);        }    return ans;}int main(){    int cas = 0 ;    int T;    scanf("%d",&T);    while(T--)    {        int m;        scanf("%d%d",&n,&m);        int a,b;        for(int i =1;i<=n;i++)            G[i].clear();        for(int i = 1;i<=m;i++)        {            scanf("%d%d",&a,&b);            G[a].push_back(b);            G[b].push_back(a);        }        memset(vis,0,sizeof(vis));        tot = -1;        for(int i = 1;i<=n;i++)            if(!vis[i])                cnt[++tot] = dfs(i);        for(int i = 0;i<=tot;i++)            d[i].clear();        printf("Case %d: %.6f\n",++cas,dp(0,1));    }    return 0;}