題意:
給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;
}