[XTU][1485][Beautiful Tree][树形DP + 背包 + 二分]

题目意思为有一棵树,对每个结点有两个值 v[i] 和 w[i],让你在这树上找一棵子树,使得 sum{ v[i] }/ sum { w[i] } 最大。二分这个答案 r ,对每个结点,重新计算一个值  t[i]= v[i]- r* w[i]。问题变成判定是否存大一棵子树,使得 t[i] 权值和大于0。

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

int const N= 110;
int v[N], w[N], flag[N];
int n, k;
double val[N];

double const inf= -1e300;

vector<int> mat[N];
double dp[N][N];
int isok= 0;

int dfs( int u ){
	flag[u]= 1;
	
	int num= 1; dp[u][1]= val[u];
	for( int i= 0; i< mat[u].size(); ++i ){
		int v= mat[u][i];
		
		if( flag[v] ) continue;
		
		int tmp= dfs(v); num+= tmp;
		
		for( int j= n; j> 0; j-- ){
			for( int t= 1; t< j; ++t )
			if( dp[u][j-t]+ dp[v][t]> dp[u][j] )
			dp[u][j]= dp[u][j-t]+ dp[v][t];
		}
	}
	
	return num;
}

void solve(){
	double left= 0, right= 10000000;
	while( right- left> 1e-4 ){
		double mid= (left+ right)/ 2.0;
		
		for( int i= 1; i<= n; ++i )val[i]= 1.0* v[i]- mid* w[i];
		
		isok= 0;
		for( int i= 0; i<= n; ++i )
		for( int j= 0; j<= n; ++j )
		dp[i][j]= inf;
		
		for( int j= 0; j<= n; ++j ) flag[j]= 0;
		dfs( 1 );
		
		for( int i= 1; i<= n; ++i )
		for( int j= k; j<= n; ++j )
		if( dp[i][j]>= 0 ) isok= 1;
		
		if( isok ) left= mid;
		else right= mid;
	}
	
	printf("%.2lf\n", left );
}

int main(){
	int test;
	scanf("%d",&test );
	while( test-- ){
		scanf("%d%d",&n,&k );
		
		for( int i= 1; i<= n; ++i ) scanf("%d", v+ i );
		for( int j= 1; j<= n; ++j ) scanf("%d", w+ j );
		
		for( int i= 0; i<= n; ++i ) mat[i].clear();
		
		for( int i= 1; i< n; ++i ){
			int u, v;
			scanf("%d%d",&u,&v );
			
			mat[u].push_back(v);
			mat[v].push_back(u);
		}
		
		solve();
	}
	
	return 0;
}
 

你可能感兴趣的:(J#)