UVa 11600 Masud Rana

期望dp

将有怪物的边视作断边,经过一条边则将边的两端连通

那么求的是任意两点彼此连通的期望天数

也就是从1开始可以到达任意一点

由于题目开始就连了一些边,所以将各个连通块视作点,并记录连通块的大小

为了方便,将1所在的连通块编号为1

记  f[s] 表示当前可到达的连通块集合为 s, 期望还需多少天可以覆盖全集

讨论下一个前往的连通块是否在集合内得到转移方程:

// i 为下一个前往的连通块编号, tot表示当前集合点数, s[i]表示编号为 i 的连通块大小

f[s] = (f[s]+1)*(tot-1)*(n-1) + (f[s|(1<<i-1)]+1)*s[i]/(n-1)

将 f[s]移到左边就可以算出 f[s]

注意特判n==1的情况,边界条件:当tot == n是答案为0

题目数据不强,可以用记忆化搜索通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 35;
 5 
 6 int T, n, m, kase, e[N][N], siz[N], cnt;
 7 bool vis[N];
 8 
 9 map<int, double> f;
10 
11 double dp(int s) {
12     if(f[s] > 0) return f[s];
13     int tot = 0;
14     for(int i = 0; i < cnt; i++)
15         if(s&(1<<i)) tot += siz[i + 1];
16     if(tot == n) return 0;
17     f[s] = 1.0*(tot - 1)/(n - 1);
18     for(int i = 1; i <= cnt; i++) 
19         if(!(s&(1<<(i - 1)))) 
20             f[s] += (dp(s|(1<<(i - 1))) + 1)*1.0*siz[i]/(n - 1);
21     f[s] = f[s]*1.0*(n - 1)/(n - tot);
22     return f[s];
23 }
24 
25 int dfs(int u) {
26     int res = 1;
27     vis[u] = 1;
28     for(int v = 1; v <= n; v++)
29         if(!vis[v] && e[v][u]) res += dfs(v);
30     return res; 
31 }
32 
33 int main() {
34     scanf("%d", &T);
35     while(T--) {
36         printf("Case %d: ", ++kase);
37         scanf("%d%d", &n, &m);
38         memset(e, 0, sizeof(e));
39         memset(vis, 0, sizeof(vis));
40         for(int u, v, i = 1; i <= m; i++) {
41             scanf("%d%d", &u, &v);
42             e[u][v] = e[v][u] = 1;
43         }
44         cnt = 0;
45         f.clear();
46         for(int i = 1; i <= n; i++)
47             if(!vis[i]) siz[++cnt] = dfs(i);
48         printf("%.7f\n", dp(1));
49     }
50 }
View Code

猜你喜欢

转载自www.cnblogs.com/ympc2005/p/12336452.html