zoukankan      html  css  js  c++  java
  • 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
    ",i-1);
      return 0;
    } 
  • 相关阅读:
    CSS优化,提高性能的方法有哪些?
    稀疏数组(SparseArray)(Go)
    Go
    Vue 实战-6 rest 重置表单不生效原因
    Go
    Vue 实战-5 批量导出 excel功能
    Vue 实战-4 表格展开行
    Vue 实战-3 vue 中使用watch 监听 el-input属性值
    Vue 实战-2 输入框加搜索图标
    Vue 实战-1 去掉 input [number] 默认增减箭头样式
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6624756.html
Copyright © 2011-2022 走看看