题意:
给定一棵树,寻找一个路径,将断掉所有与这个路径上的点相连的边,使得剩下的最大连通块的大小最小
题解:
这题有点印象,感觉做过,至少这个方法肯定遇到过
设dp[u]表示以u为根的子树里,删除以u为起点的路径后最大连通块的大小最小是多少
我们贪心的去进行转移,它一定是选择最大儿子去走路径去删除,因为只有这样才会影响到最大连通块(最大儿子意味着最大连通块)
所以转移方程为:dp[u]=max(sz[v2],dp[v1]),最大连通块要么是以v1为根删除得到的最大连通块,要么就是子树v2
但是这个dp[i]并不是答案
因为路径并不是只从u开始,有可能路径的两端都在一个子树里
此时,路径一定是以u为根的子树里,,因为有两个方向的路径,因此一定走最大儿子和次大儿子。此时连通块有这几部分:以v1为根的子树里删除后剩下的连通块,以v2为根的子树里删除后剩下的连通块,第三大叶子节点v3,还有就是除了以u为根的子树,之外所有点组成一个连通块,这四部分取max,答案我们取min
此时答案就是:ans=min(ans,max(n-sz[u],dp[v1],dp[v2],sz[v3]))
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e6;
vector<int> v[maxn];
int n;
int dp[maxn];
int sz[maxn];
int res= 1e9;
void dfs(int u, int fa)
{
sz[u]= 1;
dp[u]= 1;
int maxv= 0;
int maxv2= 0;
int maxv3= 0;
int id= 0; //大儿子
int id2= 0; //次大儿子
for (auto j : v[u]) {
if (j == fa)
continue;
dfs(j, u);
sz[u]+= sz[j];
if (sz[j] > maxv) {
maxv3= maxv2;
maxv2= maxv;
maxv= sz[j];
id2= id;
id= j;
}
else if (sz[j] > maxv2) {
id2= j;
maxv3= maxv2;
maxv2= sz[j];
}
else if (sz[j] > maxv3) {
maxv3= sz[j];
}
}
int ans= 0;
dp[u]= max(maxv2, dp[id]);
res= min(res, max({n - sz[u], maxv3, dp[id], dp[id2]}));
}
int main()
{
cin >> n;
for (int i= 0; i < n - 1; i++) {
int a, b;
scanf("%d%d", &a, &b);
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1, 0);
cout << res << endl;
return 0;
}