ACM模版

描述


题解

很神的一个期望状压树型 DP

第一步:进行缩点,因为有的点本身就是联通的,所以缩成一个点 x ,并用 cnt[x] 表示这个缩点所拥有的城市个数;

第二步:设 dp[u][st] 表示目前在 u 点的一个城市经过了 st 状态的缩点,还需要多少天才能消灭妖怪使满足道路通畅,因为这里 1N30 ,比较大,直接开数组没法开,并且没法使用滚动数组, 所以使用 map 存储。

现在来分析一下状态转移关系,设 n 为当前经过了 st 状态的城市个数,那么先考虑搞定这 n 个点所需要的时间,平均需要走 N1Nn 次才能搞定这些城市;当然此时还没有考虑完毕,还有树型部分的情况,那就是没有访问过的节点,走到节点 i 的概率为 cnt[i]Nn ,若走到 i 节点,还平均需要 dp[i][st | (1<<i)] ,则

dp[u][st]=N1Nn+st & (1 << i) == 0cnt[i]Nndp[i][st | (1<<i)]

这个状态转移部分着实不好想,期望很玄……头疼。

保存六位小数……

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>

#define INF 0x3f3f3f3f
#define MAXN 32
#define MAXM 500

using namespace std;

int T, N, M, tot;
int vis[MAXN];
int cnt[MAXN];
int mp_[MAXN][MAXN];
map<int, double> dp[MAXM];

void dfs(int x)
{
    vis[x] = 1;
    cnt[tot]++;
    for (int i = 1; i <= N; i++)
    {
        if (mp_[x][i] && !vis[i])
        {
            dfs(i);
        }
    }
}

int num(int x)
{
    int ret = 0;
    for (int i = 0; i < tot; i++)
    {
        if (x & (1 << i))
        {
            ret += cnt[i];
        }
    }

    return ret;
}

double get_dp(int u, int st)
{
    if (dp[u].count(st))
    {
        return dp[u][st];
    }

    double &ans = dp[u][st];
    int n = num(st);
    if (n == N)
    {
        return ans = 0;
    }

    ans = 1.0 * (N - 1) / (N - n);
    for (int i = 0; i < tot; i++)
    {
        if (!(st & (1 << i)))
        {
            ans += get_dp(i, st | (1 << i)) * cnt[i] / (N - n);
        }
    }
    return ans;
}

int main()
{
    scanf("%d", &T);

    for (int ce = 1; ce <= T; ce++)
    {
        scanf("%d%d", &N, &M);

        memset(mp_, 0, sizeof(mp_));
        memset(vis, 0, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));

        int u, v;
        while (M--)
        {
            scanf("%d%d", &u, &v);
            mp_[u][v] = mp_[v][u] = 1;
        }

        // 缩点
        tot = 0;
        for (int i = 1; i <= N; i++)
        {
            if (!vis[i])
            {
                dfs(i);
                tot++;
            }
        }

        for (int i = 0; i < N; i++)
        {
            dp[i].clear();
        }
        printf("Case %d: %.6lf\n", ce, get_dp(0, 1));
    }

    return 0;
}