[JSOI2016]最佳团体

输入输出样例

输入样例#1:

1 2
1000 1 0
1 1000 1

输出样例#1:

0.001


一看到这种最大化比值的问题应该就能想到分数规划吧

但是好久没写了,这次再推一下加深记忆

(frac{sum_{i=1}^{n}{p[i]}}{sum_{i=1}^{n}{s[i]}}=k)

(k*sum_{i=1}^{n}{s[i]}=sum_{i=1}^{n}{p[i]})

(sum_{i=1}^{n}{p[i]}-k*sum_{i=1}^{n}{s[i]} == 0)

所以我们就二分这个k

然后选择每个人的价值就变成了([i]-k*s[i])

直接树上依赖背包就好辣

最大化这个值,看看最后(f[1][m])是否大于这个值

还有就是这题卡常数,我被卡了好几发50

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 2505 ;
const double eps = 1e-4 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n , m ;
double s[M] , p[M] , Ans ;
int hea[M] , num , size[M] ;
double f[M][M] , res ;
struct E { int Nxt , to ; } edge[M << 1] ;
inline void add_edge(int from , int to) {
    edge[++num].Nxt = hea[from] ;
    edge[num].to = to ;
    hea[from] = num ;
}
void Dfs(int u) {
    f[u][1] = p[u] - res * s[u] ; f[u][0] = 0 ; size[u] = 1 ;
    for(int i = hea[u] , v ; i ; i = edge[i].Nxt) {
        v = edge[i].to ;
        Dfs(v) ;
        size[u] += size[v] ;
        for(int j = size[u] ; j >= 0 ; j --)
            for(int k = 0 ; k <= size[v] && k < j ; k ++)
                f[u][j] = max(f[u][j] , f[u][j - k] + f[v][k]) ;
    }
}
inline bool chk(double mid) {
    res = mid ;
    memset(f , -63 , sizeof(f)) ;
    Dfs(1) ;
    return (f[1][m] >= 0) ;
}
int main() {
    m = read() + 1 ; n = read() + 1 ;
    for(int i = 2 , x ; i <= n ; i ++) {
        s[i] = read() ; p[i] = read() ; x = read() + 1 ;
        add_edge(x , i) ;
    }
    double l = 0 , r = 1e4 , mid ;
    while(r - l >= eps) {
        mid = (l + r) / 2.0 ;
        if(chk(mid)) l = mid , Ans = mid ;
        else r = mid ;
    }
    printf("%.3lf
",Ans) ;
    return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/9703123.html