P1642 规划 01分数规划+树形DP

$ \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\n", l);
	return 0;
}
posted @ 2019-04-04 09:12  olinr  阅读(155)  评论(0编辑  收藏  举报