\(\mathcal {NOIP2018} 旅行 - 竞赛题解\)

坑还得一层一层的填

填到Day2T1了 洛谷 P5022


题目

(以下copy自洛谷,有删减/修改 (●ˇ∀ˇ●))

题目描述

小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。

小Y了解到, X国的 \(n\) 个城市之间有 \(m\) 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。

小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。

为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 \(n\) 的序列。她希望这个序列的字典序 最小,你能帮帮她吗? 对于两个长度均为 \(n\) 的序列 \(A\) 和 \(B\),当且仅当存在一个正整数 \(x\),满足以下条件时, 我们说序列 \(A\) 的字典序小于 \(B\)。

对于任意正整数 \(1 ≤ i < x\),序列 \(A\) 的第 \(i\) 个元素 \(A_i\) 和序列 BB 的第 \(i\) 个元素 \(B_i\) 相同。

序列 \(A\) 的第 \(x\) 个元素的值小于序列 \(B\) 的第 \(x\) 个元素的值。

输入输出格式

输入格式:

输入文件共 \(m + 1\) 行。第一行包含两个整数 \(n,m(m = n 或 n+1)\),中间用一个空格分隔。

接下来 m 行,每行包含两个整数 \(u,v (1 ≤ u,v ≤ n)\) ,表示编号为 \(u\) 和 \(v\) 的城市之 间有一条道路,两个整数之间用一个空格分隔。

输出格式:

输出文件包含一行,\(n\) 个整数,表示字典序最小的序列。相邻两个整数之间用一个 空格分隔。


解析

这道题特殊点在于它的边数 \(m\) 只能为 \(n\) 或 \(n+1\),而且是一个连通图,无重边自环!这就意味着它要么是一棵树,要么是只带有一个环的“树”……这是什么“树”?——基环树(我也是第一次用这个东西QwQ)

因为要让字典序最小,所以肯定是从城市 \(1\) 开始。

首先考虑树的情况:

贪心的,我们只要让下一步到达的点尽可能小就可以了,所以对于每一个点 \(u\) ,将与它相连的点按编号大小排序(即将邻接表排序),当然也可以直接用 \(set\) 存邻接表,反正我是这么干的。然后按照节点编号大小,走没有访问过的编号最小的节点就可以了,最后一定可以把整棵树遍历完(非常easy?)

然后就是有一个环的情况了(考场上一直在想一次DFS \(O(nlog_n)\) [1]贪心……就没有想过 \(O(n^2)\) 也能过)。

有一个环,但是我们最多只会走 \(n-1\) 条边(不能重复经过点嘛)……那把这个环拆开——删除环上的一条边,不就又变成一棵树了吗?(基环树的一般操作)

显然我们可以一次DFS找到那一个环上的所有边(\(O(n)\)),我们把找到的环的 边的编号 存储在 \(cir[]\) 数组里。根据上面的思想,我们可以枚举删除 \(cir[i]\) ,然后按照之前树的做法贪心的求出删除 \(cir[i]\) 的时候的最小答案,对于每次求出的答案取最小值就可以了。

具体怎么做?先枚举 \(cir[i]\) (\(O(n)\)),将标记 \(del\) 赋值为 \(cir[i]\),然后从节点 1 出发遍历所有点 (\(O(n)\)),当发现要经过的边的编号为 \(del\) 时,就 continue,不走该边。所以总的时间复杂度为 \(O(n^2)\) 。

额……不过STL的诡异常数好像在洛谷上有一点卡常QwQ,用set的做法在洛谷上只能开 O2 优化才能过,还是推荐直接将邻接表排序。


源代码

(这里还是把用set写的代码粘上来)

  1. /*Lucky_Glass*/
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. const int N=5000;
  5. set< pair< int,int > > lnk[N+5];
  6. int n,m,del;
  7. int ans[N+5],res[N+5]; //ans为最终答案,res为当前贪心得到的答案
  8. bool Prefer(){ //判断ans和res谁更优
  9. for(int i=1;i<=n;i++){
  10. if(ans[i]<res[i]) return false;
  11. if(ans[i]>res[i]) return true;
  12. }
  13. return false;
  14. }
  15. int vis[N+5],tag;
  16. void DFS(int u){
  17. vis[u]=tag;
  18. res[++res[0]]=u;
  19. if(res[0]==n && Prefer())
  20. memcpy(ans,res,sizeof res);
  21. for(set< pair< int,int > >::iterator it=lnk[u].begin();it!=lnk[u].end();it++){
  22. int v=it->first,id=it->second;
  23. if(vis[v]==tag || id==del) continue; //如果是访问过的点或者经过了删除了的边
  24. DFS(v);
  25. }
  26. }
  27. int cir[N+5],beg,ok;
  28. void Circle(int u,int pre){
  29. vis[u]=tag;
  30. for(set< pair< int,int > >::iterator it=lnk[u].begin();it!=lnk[u].end();it++){
  31. int v=it->first,id=it->second;
  32. if(v==pre || id==cir[1]) continue; //如果是父亲,或者是环的末端
  33. if(vis[v]==tag){
  34. beg=v,ok=true,cir[++cir[0]]=id; //找到环,退出
  35. break;
  36. }
  37. Circle(v,u);
  38. if(ok) cir[++cir[0]]=id; //存储环
  39. if(u==beg) ok=false; //完成整个环的搜索,结束存储
  40. }
  41. }
  42. int main(){
  43. ans[1]=(1<<29); //将ans的字典序初始化为最大
  44. scanf("%d%d",&n,&m);
  45. for(int i=1;i<=m;i++){
  46. int u,v;scanf("%d%d",&u,&v);
  47. lnk[u].insert(make_pair(v,i)); //用pair的first存储相邻点,second存储边的编号
  48. lnk[v].insert(make_pair(u,i)); //这样就可以直接给邻接表排序
  49. }
  50. if(n==m){
  51. tag=-1; //一个简单的vis标记,只要vis[u]!=tag,那么u在这次搜索中就没有被访问过(可以减去一次memset)
  52. Circle(1,-1); //找环
  53. for(int i=1;i<=cir[0];i++){ //枚举删边
  54. del=cir[i];tag=i;res[0]=0; //注意清空res
  55. DFS(1);
  56. }
  57. }
  58. else{
  59. del=-1;tag=1; //不删除任何点
  60. DFS(1);
  61. }
  62. for(int i=1;i<=n;i++)
  63. printf("%d%c",ans[i],i==n?'\n':' ');
  64. return 0;
  65. }

\(\mathcal {THE\ END}\)

\(\mathcal {Thanks\ For\ Reading!}\)

有什么没看懂的可以随时在 \(lucky\_glass@foxmail.com\) 邮箱问我 (。・∀・)ノ


  1. 这里的 \(O(log_n)\) 的复杂度来源于对邻接表的排序操作 ↩︎

竞赛题解 - NOIP2018 旅行的更多相关文章

  1. 竞赛题解 - NOIP2018 保卫王国

    \(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...

  2. 竞赛题解 - NOIP2018 赛道修建

    \(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解 额--考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ 现在把坑填好了~ 题目 (Copy from 洛谷) 题 ...

  3. 《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

    这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju/poj/uva的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就 ...

  4. 【LG5022】[NOIP2018]旅行

    [LG5022][NOIP2018]旅行 题面 洛谷 题解 首先考虑一棵树的部分分怎么打 直接从根节点开始\(dfs\),依次选择编号最小的儿子即可 而此题是一个基环树 怎么办呢? 可以断掉环上的一条 ...

  5. 竞赛题解 - Karp-de-Chant Number(BZOJ-4922)

    Karp-de-Chant Number(BZOJ-4922) - 竞赛题解 进行了一次DP的练习,选几道题写一下博客~ 标签:BZOJ / 01背包 / 贪心 『题目』 >> There ...

  6. 竞赛题解 - Broken Tree(CF-758E)

    Broken Tree(CF-758E) - 竞赛题解 贪心复习~(好像暴露了什么算法--) 标签:贪心 / DFS / Codeforces 『题意』 给出一棵以1为根的树,每条边有两个值:p-强度 ...

  7. 竞赛题解 - Palisection(CF-17E)

    Palisection(CF-17E) - 竞赛题解 Manacher学到一定程度,也需要练一下有趣的题了-- (这是多老的题了 \(QwQ\))[传送门] 『题意』 给出一个字符串,求总共有多少对不 ...

  8. 竞赛题解 - [CF 1080D]Olya and magical square

    Olya and magical square - 竞赛题解 借鉴了一下神犇tly的博客QwQ(还是打一下广告) 终于弄懂了 Codeforces 传送门 『题目』(直接上翻译了) 给一个边长为 \( ...

  9. 竞赛题解 - CF Round #524 Div.2

    CF Round #524 Div.2 - 竞赛题解 不容易CF有一场下午的比赛,开心的和一个神犇一起报了名 被虐爆--前两题水过去,第三题卡了好久,第四题毫无头绪QwQ Codeforces 传送门 ...

随机推荐

  1. VS code 自定义快捷输入

    本文是从简书复制的, markdown语法可能有些出入, 想看"正版"和更多内容请关注 简书: 小贤笔记 位置 ctrl+shift+p 搜索: snippets 输入类型: 比如 ...

  2. 用C语言指针作为函数返回值

    转载:http://c.biancheng.net/cpp/html/3242.html C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数.下面的例子定义了一个函数 strlon ...

  3. Junit 报错: Failed to load ApplicationContext

    今天在使用Junit测试时候,报了个错误: Failed to load ApplicationContext, aspect not found;挺奇怪的 我又没有调用你,之前还好好的,现在不能使用 ...

  4. Linux下mysql安装过程

    到mysql官网下载mysql编译好的二进制安装包,在下载页面Select Platform:选项选择linux-generic,然后把页面拉到底部,64位系统下载Linux - Generic (g ...

  5. 我的前端页面开发js简易有效环境

    前端开发主要涉及到html, css(less/sass), javascript这几个方面的知识.真正的快速有效开发,必须实现所谓所见即所得.在构建生产时,可能需要使用gulp/grunt等task ...

  6. SVN升级到1.8后 Upgrade working copy

    SVN升级到1.8后没法用了,不能提交,提示说要SVN Upgrade working copy, 但是半天在根目录和.svn所在文件夹上面右键都没有找到这个菜单. 坑爹的…… 最后找到解决办法是:重 ...

  7. 一次查找sqlserver死锁的经历

    查找bug是程序员的家常便饭,我身边的人喜欢让用户来重现问题.当然他们也会从正式服务器上下载错误log,然后尝试分析log,不过当错误不是那种不经思考就可识别的情况,他们就会将问题推向用户,甚至怪罪程 ...

  8. UID卡修改&UID锁死修复

    好久没发RFID类文章,最近有小伙伴问到UID卡的问题,在这里就写一写吧. 首先是UID修改的问题,只要卡是UID卡,就都可以修改UID,首先读卡器连接电脑,卡片放到读卡器上. 然后我们要用一个工具, ...

  9. 服务容错处理库Polly使用

    服务容错处理库Polly使用 在进入SOA之后,我们的代码从本地方法调用变成了跨机器的通信.任何一个新技术的引入都会为我们解决特定的问题,都会带来一些新的问题.比如网络故障.依赖服务崩溃.超时.服务器 ...

  10. poj2312 Battle City 【暴力 或 优先队列+BFS 或 BFS】

    题意:M行N列的矩阵.Y:起点,T:终点.S.R不能走,走B花费2,走E花费1.求Y到T的最短时间. 三种解法.♪(^∇^*) //解法一:暴力 //157MS #include<cstdio& ...