题目链接:规划
这个式子显然可以01分数规划,然后问题就变成选n-m个点,并且是一个联通块的最大权值,树形dp即可。
dp[i][j] 为以 i 为根节点,选取 j 个点的最大值。然后树上做背包即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=110;
int n,m,sz[N]; double dp[N][N],a[N],b[N],res;
vector<int> g[N];
void dfs(int x,int fa,double mid){
dp[x][1]=a[x]-mid*b[x]; sz[x]=1;
for(int to:g[x]) if(to!=fa){
dfs(to,x,mid);
for(int j=sz[x];j>=1;j--) for(int k=sz[to];k>=1;k--)
dp[x][j+k]=max(dp[x][j+k],dp[x][j]+dp[to][k]);
sz[x]+=sz[to];
}
res=max(res,dp[x][n-m]);
}
inline int check(double mid){
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=-1e9;
res=-1e9; dfs(1,0,mid);
return res>0;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1,a,b;i<n;i++) cin>>a>>b,g[a].push_back(b),g[b].push_back(a);
double l=0.0,r=1e6;
for(int i=1;i<=50;i++){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.1lf\n",l);
return 0;
}