规划 洛谷p1642

题目描述

某地方有N个工厂,有N-1条路连接它们,且它们两两都可达。每个工厂都有一个产量值和一个污染值。现在工厂要进行规划,拆除其中的M个工厂,使得剩下的工厂依然连成一片且 总产量/总污染 的值最大。

输入输出格式

输入格式:

 

第一行N M(1<N<100,1<=M<N),表示工厂个数和要拆除的个数。

第二行N个正整数,表示每个工厂的产值[1..10000]

第三行N个正整数,表示每个工厂的污染值[1..10000]

接着N-1行,每行两个正整数a b(1<=a,b<=N)表示a,b之间相连。

 

输出格式:

 

总产量/总污染 的最大值,保留一位小数。

 

输入输出样例

输入样例#1: 复制

3 2
2 3 4
1 1 1
1 2
2 3

输出样例#1: 复制

4.0

 

题解:01分数规划二分答案,树形dp求包含(n-m)个节点的最大子树和。

#include<bits/stdc++.h>
#define f(i,l,r) for(i=(l);i<=(r);i++)
#define ff(i,r,l) for(i=(r);i>=(l);i--)
using namespace std;
const int MAXN=105,INF=1e20;
double EPS=1e-3;
struct Node{
	int v,w;
	double c;
}a[MAXN];
struct Edge{
	int v,nxt;
}e[MAXN<<1];
int n,m;
double f[MAXN][MAXN];
int h[MAXN],tot,size[MAXN],flag;
inline void add(int u,int v)
{
	e[tot].v=v;
	e[tot].nxt=h[u];
	h[u]=tot++;
}
void dfs(int u,int fa)
{
	int i,j,k;
	size[u]=1;
	f[u][1]=a[u].c;
	for(i=h[u];~i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);
		if(flag) return;
		size[u]+=size[v];
		int max_j=min(n-m,size[u]);
		ff(j,max_j,2){
			f(k,1,j-1){
				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
			}
		}
	}
	if(f[u][n-m]>=0){
		flag=1;
	}
}
bool check(double ans)
{
	int i,j;
	f(i,1,n){
		a[i].c=1.0*a[i].v-ans*a[i].w;
	}
	f(i,1,n){
		f(j,1,n){
			f[i][j]=-INF;
		}
	}
	flag=0;
	dfs(1,-1);
	return flag;
}
int main()
{
	ios::sync_with_stdio(false);
	memset(h,-1,sizeof(h));
	int i,j,u,v;
	double l=0,r=10000;
	cin>>n>>m;
	f(i,1,n){
		cin>>a[i].v;
	}
	f(i,1,n){
		cin>>a[i].w;
	}
	f(i,1,n-1){
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	while(r-l>EPS){
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	cout<<fixed<<setprecision(1)<<l<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值