首页 > 代码库 > vijos 1476 旅游规划题解

vijos 1476 旅游规划题解

题目链接:https://vijos.org/p/1476

解:因为这一定是一棵树,所以我们多画几次图,就会发现所有的最长路径中心点都一样,且中心点把这条最长路径分成两段等长的路。

那么做法就很简单啦,先求出图的最长路径长度(称为直径),然后找到中心点(如果最长路径长度为偶数的话,就新建一个点,连上中间的两个点,并把原来两点间的路径删去),然后做一次dfs,那些到中心点的距离为半径的,这个点包括它到中心点的路径上的点都是在最长路径上的。

求最长路径有两种方法:

1.随便取一个点为根,先做一次dfs,再找一个深度最大的点为根,再做一次dfs就可以得到最大的深度了(即为最长路径)。

2.这个有点像拓扑排序,我们每次删除都把度为1的点以及相连的边删去,直到剩下点的数目小于等于二,最后最长路径的长度即为删除的次数(删掉一批度为零的点算一次)*2+剩余的点数目。原理其实就是我们每次把最长链的两端删掉,直到没法删为止,那么删除次数*2+剩余点即为答案。

那么看一下具体程序吧。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct ding{
    int to,next;
}edge[500000];
int ch,nex,cnt,maxdep2,maxdep=0,n,dep[300000],dep2[300000],head[300000],root;
bool b[300000];
void dfs(int x,int d)
{
  dep[x]=d;
  if (d>maxdep) {maxdep=d;ch=x;}
  for (int i=head[x];i;i=edge[i].next) if (dep[edge[i].to]==0) dfs(edge[i].to,d+1);
}
int dfs2(int x,int d)
{
  if (d==maxdep/2) return x;
  for (int i=head[x];i;i=edge[i].next)
  if (dep[edge[i].to]<dep[x])  return (dfs2(edge[i].to,d+1));
}
void dfs3(int x)
{
  b[x]=true;
  for (int i=head[x];i;i=edge[i].next)
//保证那个点之前是没被更新的过,且那个点是当前点的父节点
  if ((!b[edge[i].to])&&(dep[edge[i].to]<dep[x]))  
dfs3(edge[i].to);
}
void add(int u,int v){edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}
int main()
{
  scanf("%d",&n);
  int s,t;
  for (int i=1;i<=n-1;i++)
  {
    scanf("%d%d",&s,&t);
    add(s+1,t+1);add(t+1,s+1);//为了方便处理
  }
  dfs(1,1); 
  int ch2=ch; ch=0;
  memset(dep,0,sizeof dep); maxdep=0; dfs(ch2,1);
//用两次dfs得出最长链长度
  int now=dfs2(ch,1);
//从链尾返回,找到中心点的前一个点
//分类讨论
  for (int i=head[now];i;i=edge[i].next) if (dep[edge[i].to]<dep[now]) nex=edge[i].to;
//nex代表当前找到的点后面的点。
//如果路的长度为偶数的话,我们就需要加边,删边,加点,这里我加了一个n +1的点
  if (maxdep%2==0)
{add(nex,n+1);add(n+1,nex);add(n+1,now);add(now,n+1);root=n+1;}
  else root=nex;
//如果长度为奇数,那么中心点就是nex
//删边
  if (maxdep%2==0)
  {
    for (int i=head[now];i;i=edge[i].next)
      if (edge[edge[i].next].to==nex) 
      {
        edge[i].next=edge[edge[i].next].next;
        break;
      }
    for (int i=head[nex];i;i=edge[i].next)
      if (edge[edge[i].next].to==now) 
      {
        edge[i].next=edge[edge[i].next].next;
        break;
      }
  }
//得到半径,并进行第三次dfs
  int de=maxdep/2+1; maxdep=0; memset(dep,0,sizeof dep); dfs(root,1);
//如果深度等于半径的话,这条路上的点都属于最长路径,我们更新答案
  for (int i=1;i<=n;i++) if (dep[i]==de)  dfs3(i);
  for (int i=1;i<=n;i++) if (b[i]) printf("%d\n",i-1);
  return 0;
} 

 

vijos 1476 旅游规划题解