P5049 [NOIP2018 提高组] 旅行

题意:

一棵树(可能是基环树),从1出发,每到达一个新的点就记录下编号。求一种走法使得记录下来的编号字典序最小。
1≤n≤500000
m=n−1 或 m=n

题解:

如果不是基环树,那直接每次走字典序小的点即可
对于基环树:
第一个方法:
暴力删边将基环树变为一颗普通的树,然后计算答案,复杂度是O(n * n)
这个方法好像只能过P5022 [NOIP2018 提高组] 旅行 这个没加强数据的题,P5049过不了
第二个方法:
参考题解
对于基环树,我们在环上跑到一半,另一半通过回溯到你刚到这个环的起点,接着DFS就可以了
例如下图,我们从1开始,走3走2然后回溯3,走4,走5回溯4,最后走6
如果在环上回溯之后(图中是2回溯到3),剩下的就不需要特殊处理

我们把在环上的点分成三种情况:
一、其出边为环上的那个点编号是其所有未被访问的出边中最小的,如下图。
相比于6和7,4更优,所以第一种情况不需要回溯,继续环上走就行

二:其出边为环上的那个点编号是其所有未被访问的出边中不是最大也不是最小的,如下图
先走4,然后走6,不需要回溯,继续环上走

三:其出边为环上的那个点编号是其所有未被访问的出边中最大的,如下图。
先走4,再走6,再回溯2

总结一下:
当我们在环上走时,只要当其出边中,在环上的那个点的编号最大时,且比回溯后第一个走的点还大,这时才回溯,其他都不用回溯正常跑DFS即可
我们用flag标记是否需要回溯,用tmp记录当前节点中第一个比环上的出边的节点还要大的节点,以便后面判断是否回溯

代码:

方法一:

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define N 5010
using namespace std;
struct node{int to,next;
}a[2*N];
struct line{int x,y;
}l[2*N];
int tot,n,m,t,ls[N],in[N],state[N],w[N],ans[N],x,y,q[N];
bool k[N][N],v[N];
void addl(int x,int y)//加边
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;in[y]++;
}
bool topsort(){int l=0,r=0;for (int i=1;i<=n;i++) if(in[i]==1) q[++r]=i;while(l<r) {int now=q[++l];for (int i=ls[now];i;i=a[i].next){int y=a[i].to;if(in[y]>1){in[y]--;if(in[y]==1) q[++r]=y;}}}if(r==n) return true;return false;
}//拓扑求环
bool cmp(line x,line y)
{return x.y>y.y;}
void dfs(int x)//走一遍
{state[++t]=x;v[x]=true;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(k[x][y]||v[y]) continue;dfs(y);}
}
void check()//判断是否为更小字典序
{int i;bool flag=false;for(i=1;i<=n;i++)if(state[i]<ans[i]){flag=true;break;}else if(state[i]>ans[i]) return;if(!flag) return;for(;i<=n;i++)ans[i]=state[i];
}
void get_ans(int xs)//暴力删边
{int x=xs,b=0,i,last=0;do{w[++b]=x;in[x]=1;for(i=ls[x];i;i=a[i].next){int y=a[i].to;if(in[y]>1){x=y;break;}}}while(i);//记录环的每个点w[++b]=xs;for(int i=1;i<b;i++)//枚举删除的边{k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=true;memset(v,0,sizeof(v));t=0;dfs(1);check();k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=false;}
}
int main()
{memset(ans,127/3,sizeof(ans));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);l[i]=(line){x,y};l[i+m]=(line){y,x};}sort(l+1,l+1+2*m,cmp);//排序tot=1;for(int i=1;i<=2*m;i++)//加边{addl(l[i].x,l[i].y);}if(m==n-1)//普通的树{dfs(1);for(int i=1;i<=n;i++)printf("%d ",state[i]);return 0;}topsort();for(int i=1;i<=n;i++)if(in[i]>1){get_ans(i);break;}for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}

方法二:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
const int N = 500010;
int n, m, vis[N], ans[N], cnt, f[N], rings[N], flag, tmp, temp, head[N], ver[N << 1], nex[N << 1], tot;
struct Node {int x, y;
}node[N << 1];
void add (int x, int y) {ver[++ tot] = y;nex[tot] = head[x];head[x] = tot;
}
bool cmp (Node a, Node b) {return a.y > b.y;
}
inline int read () {int res = 0;char ch = getchar();while (ch < '0' || ch > '9') ch = getchar();while (ch >= '0' && ch <= '9') {res = (res << 3) + (res << 1) + (ch - 48);ch = getchar();}return res;
}
void dfs (int x) {vis[x] = 1;ans[++ cnt] = x;for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (!vis[y])dfs(y);}
}
void dfsRing (int x, int fa) {if (flag) return;if (f[x] == 0) {f[x] = fa;}else if (f[x] != fa) {while (fa != x) {rings[fa] = 1;fa = f[fa];}rings[x] = 1;flag = 1;return;}for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (y == fa) continue;dfsRing(y, x);}
}
void sDfs (int x) {vis[x] = 1;ans[++ cnt] = x;if (rings[x]) { //判断x是否在环上 int flag = 0;for (int i = head[x]; i; i = nex[i]) {if (temp) break; //temp标记环上的回溯是否执行过了,因为一旦执行过环上的回溯,那么后面就不需要在环上回溯,只需正常跑DFS即可 int y = ver[i];if (vis[y]) continue;if (rings[y]) {i = nex[i];while (vis[ver[i]]) //已经被访问过的节点跳过 i = nex[i];if (i) //i不为0即环上的出边不是最大的出边 tmp = ver[i]; //tmp记录第一个比环的出边大的那个点 else if (y > tmp) { //环上的出边是最大的出边且比我们回溯后第一次要走的节点还大 flag = 1;temp = 1;}break;}}for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (vis[y]) continue;if (rings[y] && flag) continue; //flag = 1,因此回溯,不再走环上的出边 sDfs(y);}} else {for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (vis[y]) continue;sDfs(y);}}
}
int main () {n = read();m = read();for (int i = 1; i <= m; i ++) {int u = read(), v = read();node[i].x = u;node[i].y = v;node[i + m].x = v;node[i + m].y = u;}sort(node + 1, node + 2 * m + 1, cmp);for (int i = 1; i <= 2 * m; i ++)add(node[i].x, node[i].y);if (m == n - 1) {dfs(1);for (int i = 1; i <= n; i ++)printf("%d ", ans[i]);}else {dfsRing(1, 1); //一开始先找出所有在环上的点 flag = 0;tmp = 0x3f3f3f3f;sDfs(1);for (int i = 1; i <= n; i ++)printf("%d ", ans[i]);}return 0;
}

P5049 [NOIP2018 提高组] 旅行相关推荐

  1. NOIP2018提高组比赛总结

    NOIP2018提高组比赛总结 前言 新赛季,依旧有很多失误. 在些许的遗憾和无奈中,NOIP2018,撒花结束 纵观今年的整一场NOIP,有许多值得总结的地方 正文 NOIP2018初赛 第二次参加 ...

  2. 51Nod NOIP2018提高组省一冲奖班模测训练

    51Nod NOIP2018提高组省一冲奖班模测训练 NOIP2018提高组省一冲奖班模测训练1 T1 珂朵莉的旅行 T2 奈芙莲的序列 T3 奈芙莲的护符 NOIP2018提高组省一冲奖班模测训练2 ...

  3. NOIP2018提高组省一冲奖班模测训练(三)

    NOIP2018提高组省一冲奖班模测训练(三) 自己按照noip的方式考,只在最后一两分钟交了一次 第一题过了,对拍拍到尾. 第二题不会.考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还 ...

  4. NOIP2018提高组省一冲奖班模测训练2 T3 XYK的音游

    10月22日NOIP2018提高组省一冲奖班模测训练2 T3 XYK的音游 题目描述 XYK最近入坑了一个新音游. 游戏界面上有Ñ个并排的按键,当前这首歌有米个鼓点.游戏的玩法是在鼓点的时刻移动鼠标到 ...

  5. NOIP2018提高组省一冲奖班模测训练(二)

    比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态-- 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...

  6. NOIP2018 提高组游记

    NOIP2018 提高组游记的重点不是NOIP而是游记!!! 本文分为 4 个部分: 1.关于2017, 以及自己的简介 2.noip2018游记 3.写给高一高二的学弟学妹 4.写给高三的同学和自己 ...

  7. NOIP2018提高组心路历程(AFO+自闭)

    NOIP2018提高组历程(AFO+自闭) 在不断地考试考试考试(浪浪浪)中,不知不觉,11月9号这个出征日就到来了,再出发前还是有很多小插曲的(比如刚好正面遇到她,吃好饭后还对视了一眼).随着大巴的 ...

  8. NOIP2018提高组省一冲奖班模测训练3 T2 XYG的蛋糕

    10月27日NOIP2018提高组省一冲奖班模测训练3 T2 XYG的蛋糕 题目描述 XYG要过生日了,他准备了一个n×m的矩形蛋糕请大家吃. 切蛋糕的方法如下:每次选出已经分出的某一个矩形蛋糕,一刀 ...

  9. NOIP2018提高组初赛准备

    NOIP2017提高组初赛错题 一.单项选择题(共15 题,每题1.5 分,共计22.5 分:每题有且仅有一个正确选项) 4. 2017年10月1日是星期日,1949年10月1日是( ). A. 星期 ...

最新文章

  1. UML类图、接口、包、关系
  2. 设计模式——装饰器模式
  3. ICC_lab总结——ICC_lab2:设计规划
  4. 【转】在C#中使用SQLite
  5. 一个很好用的DBHelper类(包括使用SQL语句 存储过程 事务 做相关操作) 入门级
  6. ASP.NET MVC的Razor引擎:IoC在View激活过程中的应用
  7. 忠告 | 小小对Java30岁程序猿的忠告,强烈推荐!
  8. (34)FPGA分频设计-奇数分频(第7天)
  9. iphone以旧换新活动_上苏宁易购预定iPhone12,以旧换新享5G!
  10. MNIST数据集下载与保存为图片格式
  11. 计算机文档调换顺序,word文档页面顺序调换
  12. 自制超级精简版 360网盘6.5.2.1060(7文件,体积不到6M)
  13. 一文掌握秩和比综合评价法
  14. 知识产权(笔记 1-3章)
  15. aspose.word给表格插入行或列
  16. TIBCO中国胡长城谈:中国企业工作流应用
  17. Hibernate:cannot simultaneously fetch multiple bags 解决方案
  18. modelsim与debussy的联合仿真
  19. GPT4论文翻译 by GPT4 and Human
  20. 国外2个在线web程序代码编辑网站

热门文章

  1. 50万年薪程序员,被百万网民怒喷后,却迎来大撕逼
  2. 一个人开始废掉的3种迹象
  3. layui 如何去dom_javascript 怎么去引用layui里面的方法
  4. 树莓派能直接运行python程序_树莓派怎么运行python程序
  5. php server 连接字符串,sqlServer 数据库常用连接字符串
  6. python3怎么安装gmpy2_python2/3 模块gmpy2在linux下安装
  7. 定义一个1 1=11 用c语言什么输出来,问题 A: C语言11.1(示例代码)
  8. php 输出json utf8,php json_encode utf-8中文问题
  9. win10 mysql my.cnf_MySQL配置文件无法修改的解决方法(Win10)
  10. merge函数_c语言中的merge函数