测试地址:巡逻
做法:贪心,对于K=1的情况下,只需要找树的直径(树上最长的路径),设这个直径为len,则结果为2*(n-1)-len+1。对于K=2的情况,先找树的一个直径,将答案总数tot赋值为2*(n-1)-len+1,然后把直径上所有边赋值为-1,再求一遍直径,结果为tot-len+1。不过需要注意,直径最开始要赋值为0,因为如果直径为负数,还不如在一个点上绕自环。至于直径的求法,当然就是DFS啦!存储从一点到叶节点的最长的两条不重合的路径,并用下一层的信息修正上一层,也是类似树形DP的思想。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,mx[100010]={0},smx[100010]={0},mxp[100010]={0},smxp[100010]={0};
int first[100010]={0},tot=0,ans,mp,smp,mi;
bool vis[100010]={0};
struct edge {int v,d,next;} e[200010];
void insert(int a,int b,int d)
{
e[++tot].v=b;e[tot].d=d;e[tot].next=first[a];first[a]=tot;
}
void dfs(int v)
{
vis[v]=1;
mx[v]=smx[v]=0,mxp[v]=smxp[v]=v;
for(int i=first[v];i;i=e[i].next)
if (!vis[e[i].v])
{
dfs(e[i].v);
if (mx[e[i].v]+e[i].d>mx[v])
{
smx[v]=mx[v],mx[v]=mx[e[i].v]+e[i].d;
smxp[v]=mxp[v],mxp[v]=mxp[e[i].v];
}
else if (mx[e[i].v]+e[i].d>smx[v])
{
smx[v]=mx[e[i].v]+e[i].d,smxp[v]=mxp[e[i].v];
}
}
}
bool del(int v)
{
bool flag=0;
vis[v]=1;
for(int i=first[v];i;i=e[i].next)
if (!vis[e[i].v])
{
if (del(e[i].v))
{
e[i].d=-1;
flag=1;
}
}
if (v==mp||v==smp||flag) return 1;
else return 0;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1,a,b;i<n;i++)
{
scanf("%d%d",&a,&b);
insert(a,b,1),insert(b,a,1);
}
dfs(1);
int ans=0;
for(int i=1;i<=n;i++)
{
if (mx[i]+smx[i]>ans)
{
mi=i;
mp=mxp[i];
smp=smxp[i];
}
ans=max(ans,mx[i]+smx[i]);
}
if (k==1) printf("%d",2*(n-1)-ans+1);
else
{
int basic=2*(n-1)-ans+1;
ans=0;
memset(vis,0,sizeof(vis));
del(mi);
memset(vis,0,sizeof(vis));
dfs(1);
for(int i=1;i<=n;i++)
{
ans=max(ans,mx[i]+smx[i]);
}
printf("%d",basic-ans+1);
}
return 0;
}