[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\n",Ans) ;
return 0 ;
}