1. 程式人生 > 實用技巧 >BZOJ 4753 JSOI2016 最佳團體

BZOJ 4753 JSOI2016 最佳團體

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;
}