题目大意:给出n和m,表示说有n座城市,每两座城市间有一条路,每条路上都有怪物,现在有m条路上没有怪物,给出没有怪物的路。现在任选一座城市移过去,清除路上的怪物,当所有城市可以直接连接时终止,问说需要移动次数的期望。
解题思路:首先将已经联通的城市算成是一个联通集,这样的话,就有k个联通集,k小于三十,所以可以用二进制数来表示状态,所以有d[u][s]表示说从u这个联通即开始,已经联通的状态为s的情况下,还需要多少次的期望。转移方程:link为已经联通了的城市的数量,那么n个城市,选中未联通的可能性为(n-link)/(n-1),所以就是说有平均需要(n-1)/(n-link)次会选中为联通的城市进行移动,d[u][s] =(n-1)/(n-link) + 剩下的联通集的期望。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int N = 35;
int n, m, k, v[N], c[N];
vector<int> g[N];
map<int, double> d[N];
int dfs (int u) {
v[u] = 1;
int ans = 1;
for (int i = 0; i < g[u].size(); i++) {
if (v[g[u][i]]) continue;
ans += dfs(g[u][i]);
}
return ans;
}
void init () {
scanf("%d%d", &n, &m);
int a, b;
memset(v, 0, sizeof(v));
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
g[a].push_back(b);
g[b].push_back(a);
}
k = 0;
for (int i = 1; i <= n; i++) if (!v[i]) {
d[k].clear();
c[k] = dfs(i);
k++;
}
}
double solve (int u, int s) {
if (d[u].count(s)) return d[u][s];
int link = 0;
for (int i = 0; i < k; i++) if (s&(1<<i)) {
link += c[i];
}
if (link == n) return d[u][s] = 0;
d[u][s] = (n-1)*1.0/(n-link);
for (int i = 0; i < k; i++) if ((s&(1<<i)) == 0) {
d[u][s] += solve (i, s | (1<<i)) * c[i]/(n-link);
}
return d[u][s];
}
int main () {
int cas;
scanf("%d", &cas);
for (int i = 1; i <= cas; i++) {
init();
printf("Case %d: %.6lf\n", i, solve (0, 1));
}
return 0;
}