https://ac.nowcoder.com/acm/contest/7016/E
这题真没想到
考虑一个问题,删除边对极差的影响,-------无论如何删除边都有办法不让让答案变大(可能相等)
利用二分答案,每次算出mid都跑一次dp看看能不能k条边以内完成,dp[x][i]表示以x为根的子树,所有点的数值在list[i] -- list[i] + mid之间最少删除dp[x][i]个边
ans[x]表示ans[x]极差在mid之间最少删除ans[x] t条边
具体在代码
#include<iostream> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int maxn = 2000+11; typedef long long ll; ll inf = 1e15; ll mid; ll dp[maxn][maxn]; ll list[maxn]; vector<int>G[maxn]; void add(int x,int y){ G[x].push_back(y); } int n,k; ll ans[maxn]; int dfs(int x,int fa){ //cout<<x<<endl; for(int i = 1;i<=n;i++){ if(list[x] >= list[i] && list[i] + mid >= list[x]){ dp[x][i] = 0; } else{ dp[x][i] = inf; } } for(int i=0;i<G[x].size();i++){ int p = G[x][i]; if(p == fa) continue; dfs(p,x); for(int j=1;j<=n;j++){ dp[x][j] += min(dp[p][j],ans[p] + 1); } } for(int i = 1;i<=n;i++){ ans[x] = min(ans[x],dp[x][i]); } return 0; } int jude(){ for(int i=0;i<=n;i++){ ans[i] = inf; } dfs(1,-1); return (ans[1] <= k);//可行 } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ ans[i] = inf; scanf("%lld",&list[i]); } for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } ll l = 0; ll r = 2e9+7; ll cns = 0; for(int i=0;i<111;i++){ mid = (l+r)/2; if(jude()){//可以,区间加大 cns = mid; r = mid - 1; } else{ l = mid + 1; } } printf("%lld ",cns); return 0; }