这个式子显然可以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; }