题意:
给n个节点 他们形成了最多10条链 每条最多1000的长度 每个节点有个val 你可以选择任意位置截断链 断点前的所有节点被你获得 通过题中计算公式得出你的val 问 通过随机截断 获得val的期望是多少
思路:
期望=所有方案val的和/方案数
这里明显有分层的现象 而且每层最多10个元素 因此想到状压 那么我们只要逐层统计 每层计算一下能对“所有方案val的和”产生多少贡献即可 方案数可以直接算出来 计算方法如下
对于方案数 它就等于 (amt[1]+1)*(amt[2]+1)*… amt[i]为每条链上的节点总数 这个式子就表示对于每条链有amt+1种截断方式 即 一开始就截断+在每个元素后面截断
对于val的和 我们通过每层的状态来计算(刚才也说了要状态压缩)
如果状压中该位置为1表示选中该元素 那么序列一定是这样的111111XXXXXX 即1前面一定都是1 因此对应的方案有amt-层数+1 种
如果该位置为0 那么序列一定是这样的 XXXXXXX000000 即0后面一定都是0 那么方案就有 层数 种
知道了那一层所形成的方案数 那么只需要计算一下该层的节点val和与方案数乘一下就可以了
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 10010 int next[N], vis[N], val[N], amt[10], qu[10]; double x, y; int t, n, m, tot; int main() { int i, u, v, floor, have, num; double ways, res; //freopen("1001.in", "r", stdin); //freopen("1001.out", "w", stdout); scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); memset(next, 0, sizeof(next)); memset(vis, 0, sizeof(vis)); memset(amt, 0, sizeof(amt)); tot = 0; x = 1; y = 0; for (i = 1; i <= n; i++) scanf("%d", &val[i]); for (i = 1; i <= m; i++) { scanf("%d%d", &u, &v); u++; v++; next[u] = v; vis[v] = 1; } for (i = 1; i <= n; i++) if (!vis[i]) { qu[tot] = i; for (u = i; u; u = next[u]) amt[tot]++; x *= amt[tot] + 1; tot++; } for (floor = 1;; floor++) { num = 0; for (i = 0; i < tot; i++) if (qu[i]) num++; if (!num) break; for (u = 1; u < (1 << tot); u++) { have = 0; ways = 1; res = 0; for (i = 0; i < tot; i++) { if (u & (1 << i)) { if (!qu[i]) break; res += val[qu[i]]; have++; ways *= amt[i] - floor + 1; } else ways *= min(floor, amt[i] + 1); } if (i == tot) { y += res * ways; if (have > 1) y += res * have * ways / num; } } for (i = 0; i < tot; i++) qu[i] = next[qu[i]]; } //printf("%.3f %.3f ", y, x); printf("%.3f\n", y / (x - 1)); } return 0; }
原文地址:http://blog.csdn.net/houserabbit/article/details/38469679