题意:
从一个含有n个点的树里面,要求你删除最少的点,满足剩下来的树的直径小于等于K。要求最终的图仍然是联通的。
数据范围:
树的节点数:1<=n<=2000。
思路:
最开始的时候,也就是在考试的时候,我想到的并不是正解,但是居然骗到了ACヾ(◍°∇°◍)ノ゙!!!
大概是这样的:每次从中取出一条直径(知道两个端点就好了),然后比较两个点的“影响力”。所谓影响力,因为我们知道一棵树当中可能含有多条直径,而这里的影响力就是指的以这个点为edpoints的直径的条数。然后删去“影响力”大的那个点(因为直径的两个端点总是叶子)。这样子每次都算一次直径是O(n),然后算O(n)次,就是O(n^2)的,看起来很完美,但是是错误的…可能有多个原因,但我知道其中的一个可能是当两个点的影响力相同时,我是随机选取的其中一个端点,然而可能其中的一个节点对于那些“非直径”的路径的影响力更大,然而我选择了另外一条,然后就错了。
下面进入正题:
其实需要进行K的分类讨论。我们发现,如果一棵树的直径小于等于K,那么等价于:1.当K为奇数时,枚举一条边,然后把这条边的两个端点都放在深度0,然后
d
e
p
m
a
x
<
=
⌊
K
2
⌋
dep_{max}<=\lfloor\dfrac{K}{2}\rfloor
depmax<=⌊2K⌋即可(可以fa个图自己感受一下啦);2.当K位偶数时,枚举一个点,将这个点放在深度0,然后同样满足
d
e
p
m
a
x
<
=
⌊
K
2
⌋
dep_{max}<=\lfloor\dfrac{K}{2}\rfloor
depmax<=⌊2K⌋就可以了。(感觉自己在考场上想复杂了呢…)。
这样就是枚举一条边/点,是O(n)的;遍历整棵树,是O(n)的。整体就是O(n^2)的,得到解决。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 2000
using namespace std;
vector<int> G[MAXN+5];
int N,K,tot=0;
void DFS(int u,int fa,int depn)
{
if(depn>K)
return;
tot++;//可以计算深度小于等于K/2的,然后用总数来减,比较好想。
for(int i=0;i<(int)G[u].size();i++)
{
int v=G[u][i];
if(v==fa)
continue;
DFS(v,u,depn+1);
}
}
int main()
{
scanf("%d %d",&N,&K);
int u,v,typ;
for(int i=1;i<N;i++)
{
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
if(K%2==0) typ=1;
else typ=0;
int ans=N;
K/=2;
if(typ==1)//分情况讨论
{
for(int i=1;i<=N;i++)
{
tot=0;
DFS(i,-1,0);//把一个点放在深度0.
ans=min(ans,N-tot);
}
}
else if(typ==0)
{
for(u=1;u<=N;u++)
for(int j=0;j<(int)G[u].size();j++)
{
v=G[u][j];
tot=0;
DFS(v,u,0);//把一条边的两个点都放在深度0.
DFS(u,v,0);
ans=min(ans,N-tot);
}
}
printf("%d\n",ans);
return 0;
}