$ color{#0066ff}{ 题目描述 }$
某地方有N个工厂,有N-1条路连接它们,且它们两两都可达。每个工厂都有一个产量值和一个污染值。现在工厂要进行规划,拆除其中的M个工厂,使得剩下的工厂依然连成一片且 总产量/总污染 的值最大。
(color{#0066ff}{输入格式})
第一行N M(1<N<100,1<=M<N),表示工厂个数和要拆除的个数。
第二行N个正整数,表示每个工厂的产值[1..10000]
第三行N个正整数,表示每个工厂的污染值[1..10000]
接着N-1行,每行两个正整数a b(1<=a,b<=N)表示a,b之间相连。
(color{#0066ff}{输出格式})
总产量/总污染 的最大值,保留一位小数。
(color{#0066ff}{输入样例})
3 2
2 3 4
1 1 1
1 2
2 3
(color{#0066ff}{输出样例})
4.0
(color{#0066ff}{数据范围与提示})
none
(color{#0066ff}{题解})
显然是01分数规划问题,那么二分答案
现在的问题是找一个大小为m的联通块
经典树形DP,(f[i][j])为以i为根子树选j个点的最大值(i必选)
跑树形背包即可,注意i必选的限制,所以要对所有点取max
#include<bits/stdc++.h>
#define LL long long
LL read() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
template<class T> bool chkmax(T &a, const T &b) { return a < b? a = b, 1 : 0; }
template<class T> bool chkmin(T &a, const T &b) { return b < a? a = b, 1 : 0; }
const int inf = 0x7fffffff;
const int maxn = 555;
const double eps = 1e-6;
int n, m, a[maxn], b[maxn], siz[maxn];
double f[maxn][maxn], val[maxn], mx;
std::vector<int> G[maxn];
void dfs(int x, int fa) {
siz[x] = 1;
f[x][0] = 0, f[x][1] = val[x];
for(auto to : G[x]) {
if(to == fa) continue;
dfs(to, x);
siz[x] += siz[to];
for(int i = std::min(m, siz[x]); i >= 1; i--)
for(int j = 0; j < i; j++)
chkmax(f[x][i], f[x][i - j] + f[to][j]);
}
chkmax(mx, f[x][m]);
}
bool ok(double mid) {
for(int i = 1; i <= n; i++)
for(int j = 0; j <= m; j++)
f[i][j] = -inf;
for(int i = 1; i <= n; i++) val[i] = (double)(a[i] - mid * b[i]);
mx = -1e18;
dfs(1, 0);
return mx >= 0;
}
int main() {
n = read(), m = n - read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) b[i] = read();
int x, y;
for(int i = 1; i < n; i++) {
x = read(), y = read();
G[x].push_back(y);
G[y].push_back(x);
}
double l = 0, r = 105050;
while(r - l > eps) {
double mid = (l + r) / 2.0;
if(ok(mid)) l = mid;
else r = mid;
}
printf("%.1f
", l);
return 0;
}