zoukankan      html  css  js  c++  java
  • 洛谷 P5022 旅行——题解

      发现大部分题解都是O(n^2)的复杂度,这里分享一个O(n)复杂度的方法。

    题目传送

     

     

     

     

      首先前60%的情况,图是一棵无根树,只要从1开始DFS,每次贪心走点的编号最小的点就行了。(为什么?因为当走到一个点u时,若不把以它为根的子树的所有点都遍历一遍的话,回溯到u的父亲后,就再也没可能遍历u的没有遍历过的儿子了。)

      再看剩下40%的情况,由于题目保证图是一个无向连通图,当 边数 等于 点数减一 时图必为树,在此基础上再多加一条边,就在一棵树的基础上形成一个环(为了方便,后文仍会提到树,而后文的树指的是图没有第n条边时形成的树)。有了环会发生什么?发现有了环后第一种情况的贪心+DFS解法的依据就不成立了,即走到一个点u时,即使不把以它为根的子树的所有点都遍历一遍,当回溯到u的父亲后,也有可能会通过环的一部分到达u节点剩下的没有遍历过的儿子。显然不能再无脑贪心了。  

      仔细思考一下两种情况的不同,发现若一棵子树中没有环,也没有点能直接连向环,那这棵子树就可以用第一种情况的贪心+DFS的方法处理。若一个点u及它的儿子v都在环上,那么若要u走到v,既可以直接走u到v的连边(u,v),也可以从u开始反方向绕环一圈走到v;若u和v至少有一个点不在环上,那么从u到v只能通过边(u,v),即只有一个到达方法。这就说明,若一对父子都在环上,那他们之间有两种到达方法;否则就只有一种到达方法。

      这时两种情况的不同就明确了:同样的是:对于一个与环没有什么关系的子树(没有关系指不与环的任何一个边相交。若与环共用最多一个点,也没事。),用贪心+DFS做就好,因为当父亲回溯后未被遍历的儿子就不能再被遍历到了;不同的是,第二种情况多了父子都在环上的情况,这时父亲回溯一次后,儿子仍能被遍历。但因为“小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。”,所以每个父亲最多也只能回溯一次。

      所以我们只要搞清楚第一次环上的回溯何时发生就行了,只要发生了一次环上的回溯,第二种情况就可以当第一种情况做了(一旦环上的某个点u回溯了,那么它的儿子与它的连边就不会再用了,也相当于没有这条边,此时图只有n-1条边,就是棵树)。“环上的回溯”显然只会发生在环上(毕竟名字都说是“环上的”了),这其实就相当于在第一次环上的回溯发生前,环上的点可以“主动”发起回溯,即就算它的儿子还没有都被遍历完,它也可以回溯,不过那个没有被遍历的儿子只能是环上的点。

      思考为什么要主动回溯。我们各种乱搞,不就是为了最后的字典序最小吗?而为了达成这个目标,我们只要保证能遍历到所有点的同时,时刻最小化当前的字典序,即每次都遍历可行的编号最小的点。于是我们可以记录一下主动回溯后可以得到的最小字典序就行啦。先跑一边tarjan找到环。从第一次进入环开始就记录主动回溯后可以得到的最小字典序(sec变量),若当前点u在环上,且只剩一个同在环上的儿子了,并且儿子的编号还大于sec,那就主动回溯;不然就正常dfs就行。

    具体实现看代码吧:

      

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<queue>
      4 
      5 #define min(a,b) ((a)>(b)?(b):(a))
      6 
      7 using namespace std;
      8 
      9 const int N=5005;
     10 
     11 int n,m,x,vis[N],lst[N],xu[N],cntxu,nxt[N<<1],to[N<<1],cnt;
     12 int dfn[N],dfss,low[N],huan[N],sta[N],top;
     13 
     14 char ch;
     15 
     16 inline int read()
     17 {
     18     x=0;
     19     ch=getchar();
     20     while(!isdigit(ch)) ch=getchar();
     21     while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
     22     return x;
     23 }
     24 
     25 void tarjan(int u,int fa)
     26 {
     27     dfn[u]=low[u]=++dfss;
     28     sta[++top]=u;
     29     int Top=top;
     30     vis[u]=1;
     31     int t;
     32     for(int e=lst[u];e;e=nxt[e])
     33     {
     34         if(!dfn[t=to[e]])
     35         {
     36             tarjan(t,u);
     37             low[u]=min(low[t],low[u]);
     38         }
     39         else
     40         {
     41             if(t!=fa&&vis[t])
     42                 low[u]=min(low[t],low[u]);
     43         }
     44     }
     45     if(dfn[u]==low[u])
     46     {
     47         if(Top==top)
     48             vis[sta[top--]]=0;
     49         for(int i=Top;i<=top;++i)
     50         {
     51             huan[sta[i]]=1;//是环 
     52             vis[sta[i]]=0;
     53         }
     54         top=Top-1;
     55     }
     56 }
     57 
     58 int fir;//有没有进过环 
     59 int sec=-1;//-1标记意义为还没有进入过环 
     60 
     61 void dfs(int u)
     62 {
     63     if(vis[u]) return;
     64     priority_queue<int,vector<int>,greater<int> >hep;//用堆维护当前要dfs的最小值。由于每个节点的儿子都很少,所以时间复杂度为几乎可以忽略的常数 
     65     xu[++cntxu]=u;//记录答案序列 
     66     vis[u]=1;
     67     for(int e=lst[u];e;e=nxt[e])
     68         if(!vis[to[e]])
     69             hep.push(to[e]);
     70     int head;
     71     if(huan[u]&&!fir)
     72     {
     73         fir=1;
     74         while(!hep.empty())
     75         {
     76             head=hep.top();
     77             hep.pop();
     78             if(!huan[head]) dfs(head);//不在环上的点正常贪心DFS。 
     79             else
     80             {
     81                 if(!vis[head]&&sec==-1)
     82                 {
     83                     sec=hep.top();
     84                     dfs(head);
     85                 }
     86                 else//第一次环上回溯发生后,都正常贪心DFS 
     87                 dfs(head);
     88             }
     89         }
     90     }
     91     else
     92     {
     93         if(!huan[u]||(huan[u]&&sec==-2))
     94         {
     95             while(!hep.empty())
     96             {
     97                 if(!vis[hep.top()])
     98                     dfs(hep.top());
     99                 hep.pop();
    100             }
    101         }
    102         else
    103         {
    104             while(!hep.empty())
    105             {
    106                 head=hep.top();
    107                 hep.pop();
    108                 if(!huan[head])
    109                     dfs(head);
    110                 else
    111                 {
    112                     if(head<=sec)
    113                     {
    114                         if(!hep.empty())
    115                             sec=hep.top();
    116                         dfs(head);
    117                     }
    118                     else
    119                     {
    120                         if(hep.empty())
    121                         {
    122                             sec=-2;//主动回溯,并把sec设成-2标记第一次环上的回溯已经结束了 
    123                             return;
    124                         }
    125                         else//在环上的点,要没有 不在环上的儿子 时才能考虑主动回溯
    126                         {
    127                             sec=hep.top();
    128                             dfs(head);
    129                             while(!hep.empty())
    130                             {
    131                                 dfs(hep.top());
    132                                 hep.pop();
    133                             }
    134                         }
    135                         
    136                     }
    137                 }
    138             }
    139         }
    140     }
    141 }
    142 
    143 inline void addedge(int u,int v)
    144 {
    145     nxt[++cnt]=lst[u];
    146     lst[u]=cnt;
    147     to[cnt]=v;
    148 }
    149 
    150 int main()
    151 {
    152     n=read(),m=read();
    153     int u,v;
    154     for(int i=1;i<=m;++i)
    155     {
    156         u=read(),v=read();
    157         addedge(u,v);
    158         addedge(v,u);
    159     }
    160     tarjan(1,0);
    161     dfs(1);
    162     for(int i=1;i<=n;++i)
    163         printf("%d ",xu[i]);
    164     return 0;
    165 }

     

  • 相关阅读:
    Notes for GGX paper
    vsix dll缺失问题
    c# 引用其他工程问题
    Springboot+Maven
    http 带cookie值的Post请求(关联测试)
    http 带cookie值的get请求(关联测试)
    DefaultHttpClient 获取cookie信息
    HttpClient+ ResourceBundle接口配置优化
    Cookie和Session的区别
    moco框架——重定向
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11740479.html
Copyright © 2011-2022 走看看