BZOJ 4753 JSOI2016 最佳團體
阿新 • • 發佈:2020-08-04
BZOJ 4753 JSOI2016 最佳團體
題目描述
JSOI資訊學代表隊一共有N名候選人,這些候選人從1到N編號。方便起見,JYY的編號是0號。每個候選人都由一位
編號比他小的候選人Ri推薦。如果Ri=0則說明這個候選人是JYY自己看上的。為了保證團隊的和諧,JYY需要保證,
如果招募了候選人i,那麼候選人Ri"也一定需要在團隊中。當然了,JYY自己總是在團隊裡的。每一個候選人都有
一個戰鬥值Pi",也有一個招募費用Si"。JYY希望招募K個候選人(JYY自己不算),組成一個性價比最高的團隊。
也就是,這K個被JYY選擇的候選人的總戰鬥值與總招募總費用的比值最大。
解題思路
首先考慮分數規劃,二分這個最大的比值。
\[\frac{\sum{p}}{\sum s} \geq mid\\ \sum p \geq mid*\sum s\\ 0\geq mid *\sum s-\sum p \]
於是我們要最小化這個式子,dp即可。
典型的樹形dp。(原來我是用的樹形dp是比較少細節的,不會那麼容易退化成\(n^3\))
注意一下,初始化的時候除了0號節點,選0個點都是正無窮的代價,而在這次dfs結束的時候,要把0個節點的代價設為0,表示以後可以丟掉這個連通塊。
PS.不要使用long double 慢到懷疑人生。
#include<bits/stdc++.h> using namespace std; #define db double #pragma GCC optimize(2) const int N = 2500 + 11; int n, K, sz[N]; int wei[N], cst[N], fa[N]; int head[N], nex[N], to[N], size; db W[N], C[N], f[N][N], tmp[N]; void add(int x, int y){ to[++size] = y; nex[size] = head[x]; head[x] = size; } void dfs(int u) { if (u) f[u][1] = C[u] - W[u]; else f[u][0] = 0; sz[u] = 1; for (int i = head[u]; i; i = nex[i]) { int v = to[i]; dfs(v); for (int j = 0; j <= sz[u] && j <= K; j++) { for (int k = 0; k <= sz[v] && j + k <= K; k++) { tmp[j+k] = min(tmp[j+k], f[u][j] + f[v][k]); } } sz[u] += sz[v]; for (int j = 0; j <= K; j++) { f[u][j] = tmp[j]; tmp[j] = 1e9; } } f[u][0] = 0; } int main(){ //freopen("4753.in", "r", stdin); //freopen("4753.out", "w", stdout); cin>>K>>n; for(int i = 1;i <= n; i++){ scanf("%d%d%d", &cst[i], &wei[i], &fa[i]); W[i] = wei[i]; add(fa[i], i); } for(int i = 0;i <= K; i++)tmp[i] = 1e9; db l = 0, r = 1e4, res = 0; while(r - l > 1e-4){ db mid = (l + r) / 2.0; for(int i = 1;i <= n; i++){ C[i] = 1.0 * mid * cst[i]; } for(int u = 0;u <= n; u++) for(int i = 0;i <= K; i++)f[u][i] = 1e9; dfs(0); //printf("mid=%.3lf f=%.3lf\n", mid, f[0][K]); //cerr<<1.0 * clock() / 1000 <<endl; if(f[0][K] <= 0.0){ res = mid; l = mid + 1e-5; } else r = mid - 1e-5; //return 0; } printf("%.3lf\n", res); return 0; }