UVa 11600
有一个n个点m条边的无向图,你初始在1号点,每次移动你会等概率随机选择除了你所在的点之外的某个点,如果这两个点之间还没有边则连一条边,求使整个图联通的期望步数。
$$1 \leq n \leq 30,0 \leq m \leq \frac{n(n-1)}{2}$$
先缩点,然后设$f(S)$为当前联通块中的点为$S$这个集合时使图联通的期望步数,$\text{cnt}_S$为$S$中点数,$\text{belong}_i$为$i$所属联通块,则有
$$f(S)=\frac{n-1}{n-\text{cnt}_S} + \sum_{i \notin S} \frac{1}{n-\text{cnt}_S} f(S \cup \text{belong}_i)$$
前面的式子由几何级数推出,边界是$f(V)=0$,用哈希表存状态,记搜就可以过了。(但这样过不了极限数据,比较迷。。。)
const int MAXN = 30 + 5; struct DSU { int fa[MAXN]; void init(int n) { For(i, 1, n) fa[i] = i; } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } bool same(int x, int y) { return find(x) == find(y); } void merge(int x, int y) { if (!same(x, y)) fa[fa[y]] = fa[x]; } } U; int bel[MAXN], linker[MAXN], n, m; std::unordered_map < int, double > f; double DP(int S) { int cnt = __builtin_popcount(S); if (cnt >= n) return 0; auto it = f.find(S); if (it != f.end()) return it->S; double res = 1.0 * (n - 1) / (n - cnt); For(i, 1, n) if (!(S & (1 << i))) res += 1.0 * 1 / (n - cnt) * DP(S | linker[bel[i]]); return f[S] = res; } int main() { int _; scanf("%d", &_); For(i, 1, _) { memset(linker, 0, sizeof linker); scanf("%d%d", &n, &m); U.init(n); f.clear(); For(i, 1, m) { int u, v; scanf("%d%d", &u, &v); U.merge(u, v); } For(i, 1, n) linker[bel[i] = U.find(i)] += 1 << i; printf("Case %d: %.10f\n", i, DP(linker[bel[1]])); } return 0; }