P5022 旅行
原题链接 https://www.luogu.org/problem/P5022
本着快csp了,做点往年的NOIp的题试试水来着,没想到水这么深 难度还挺大的,耗了我一天的时间(可能是我太菜了)
题目大意:
给你 n 个点和 m 条边,问如何遍历每个结点才能使最后的字典序最小,注意只有遍历完一棵字树的所有结点后才能回溯到他的父亲结点;
前 60 pts:
作为 NOIp2018 day2T1 来说,部分分确实给的挺足的,这 60 pts 就是哦;
看到 m = n-1 说明这是一棵树,考虑用搜索:
既然要保证是字典序最小,那么我们第一个点一定要选 1 了,考虑接下来只需要从他的儿子里按照编号从小到大遍历就好了,注意要遍历完一棵子树才能回溯上去;
后 40 pts:
m = n ?嗯,基环树!
所谓基环树,就是说在一个图里,有 n 个结点和 n 条边,那么这个图内有且仅有一个环;
我们仍然可以按照前 60 pts 的做法上去想:
按理来说我们遍历完 n 个结点只需要走 n-1 条边就好了啊,所以一定有一条边是多余的,也就是说我们根本遍历不到它,那么我们可以枚举每一条边,暂时把它删掉,然后和刚才的做法一样跑一遍 dfs,这样能求得一个字典序,我们取所有字典序中最小的一个就是答案了;
优化
显然如果我们像上述做法那样暴力的话,是会有几个点 TLE 的(毕竟NOIp也不会水到这种程度吧qwq),所以我们要考虑优化;
优化一:
题目中说了,我们的目的是遍历完每个点,那么假设我们删掉的边不是环上的边,那么这个图一定会变得不连通,那么就无法完成任务了;
所以我们只要找出基环树上的环,只需删掉环上的边就好了,至于找环嘛,这里我用的 tarjan(其实是只会这个qwq)
优化二:
最优性剪枝:假如我们在 dfs 的过程中,发现求得的字典序不如之前的答案优,那么我们直接可以 return 了;
有了这两个小优化,终于可以愉快的 AC 本题了,时间复杂度 O(n2);
至于更神仙的 O(n log n)甚至 O(n)的做法,咕咕咕~
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<cmath>
- using namespace std;
- int read()
- {
- char ch=getchar();
- int a=,x=;
- while(ch<''||ch>'')
- {
- if(ch=='-') x=-x;
- ch=getchar();
- }
- while(ch>=''&&ch<='')
- {
- a=(a<<)+(a<<)+(ch-'');
- ch=getchar();
- }
- return a*x;
- }
- const int N=;
- int n,m,bi,bj,tot,top,tim,start,edge_sum,scc_sum,ans_sum=;
- int u[N],v[N],dfn[N],low[N],scc[N],st[N],vis[N],size[N],head[N],ans[N],f[N][N];
- struct node
- {
- int to,next;
- }a[N<<];
- void add(int from,int to) //链表存图
- {
- edge_sum++;
- a[edge_sum].to=to;
- a[edge_sum].next=head[from];
- head[from]=edge_sum;
- }
- void tarjan(int u,int fa) //tarjan找环
- {
- dfn[u]=low[u]=++tim;
- st[++top]=u;
- vis[u]=;
- for(int i=head[u];i;i=a[i].next)
- {
- int v=a[i].to;
- if(v==fa) continue;
- if(!dfn[v])
- {
- tarjan(v,u);
- low[u]=min(low[u],low[v]);
- }
- else if(vis[v]) low[u]=min(low[u],dfn[v]);
- }
- if(dfn[u]==low[u])
- {
- scc_sum++;
- if(u==) start=scc_sum;
- while(st[top]!=u)
- {
- vis[st[top]]=;
- scc[st[top]]=scc_sum;
- top--;
- }
- vis[st[top]]=;
- scc[st[top]]=scc_sum;
- top--;
- }
- }
- void dfs(int u,int fa)
- {
- tot++;
- if(u<ans[tot]||bj) //如果比答案更优,或者之前就已经比答案优的话,就更新答案
- {
- bj=; //发现更优解了
- ans[tot]=u;
- }
- else if(u>ans[tot]) //最优性剪枝:不如答案优就直接返回
- {
- bi=; //没有找到更优解
- return ;
- }
- vis[u]=;
- for(int i=;i<=n;i++) //从小到大去枚举每个点
- {
- if(i!=fa&&f[u][i]&&!vis[i]) //判断是否连接
- {
- dfs(i,u);
- if(bi) return ; //剪枝
- }
- }
- }
- int main()
- {
- n=read();m=read();
- for(int i=;i<=m;i++)
- {
- u[i]=read();v[i]=read();
- add(u[i],v[i]);add(v[i],u[i]); //链表存图,tarjan的时候方便
- f[u[i]][v[i]]=;f[v[i]][u[i]]=; //邻接矩阵存图,选择贪心决策的时候方便从小到大找儿子
- }
- for(int i=;i<=n;i++) //tarjan找环
- {
- if(!dfn[i]) tarjan(i,);
- }
- memset(ans,0x3f,sizeof(ans));
- if(m==n-) //前60pts
- {
- dfs(,);
- for(int i=;i<=n;i++) printf("%d ",ans[i]);
- return ;
- }
- else //后40pts:基环树
- {
- for(int i=;i<=m;i++)
- {
- if(scc[u[i]]==scc[v[i]]) //只需删除同一联通块里的边
- {
- tot=;bi=;bj=; //bi表示是否一定不会找到更优解,bj表示是否找到了更优解
- memset(vis,,sizeof(vis));
- f[u[i]][v[i]]=; //暂时删边,这里用邻接矩阵就比较方便了
- f[v[i]][u[i]]=;
- dfs(,); //更新答案
- f[u[i]][v[i]]=; //记得恢复
- f[v[i]][u[i]]=;
- }
- }
- }
- for(int i=;i<=n;i++) printf("%d ",ans[i]);
- return ;
- }
P5022 旅行的更多相关文章
- 【题解】 P5022旅行
[题解]P5022 旅行 当给定你一颗树的时候,这题就是一道送分题,凉心啊! 但是给定你一颗基环树呢? 暴力断环直接跑. 但是数据范围\(n\le 1000\) 乱做就完事了. 考场上这样想的,对于\ ...
- 【luogu P5022 旅行】 题解
题目连接:https://www.luogu.org/problemnew/show/P5022 \(NOIP2018 DAY2T1\) 考场上只写了60分,很容易想到当 m = n - 1 时的树的 ...
- 洛谷 P5022 旅行——题解
发现大部分题解都是O(n^2)的复杂度,这里分享一个O(n)复杂度的方法. 题目传送 首先前60%的情况,图是一棵无根树,只要从1开始DFS,每次贪心走点的编号最小的点就行了.(为什么?因为当走到一个 ...
- 洛谷P5022 旅行 题解 去环/搜索
题目链接:https://www.luogu.org/problem/P5022 这道题目一开始看的时候没有思路,但是看到数据范围里面有一个: \(m = n-1\) 或 \(m = n\) ,一下子 ...
- P5022 旅行 (NOIP2018)
传送门 先考虑是一颗树的情况 求最小的 dfs 序 显然按儿子编号从小到大dfs 如果有多一条边怎么办 显然会有一条边不用走 直接枚举删那条边然后每次都暴力 dfs 复杂度 $O(n^2)$ 注意每个 ...
- Luogu P5022 旅行
开始写复赛题了 先放张图纪念我惨烈的卡常之路 不说了,简直悲伤 题目链接 思路么..不想写了 Code //不要在意四十行超级加速,卡常用的 #include<bits/stdc++.h> ...
- P5022 旅行[基环树]
以后必须学会面向数据编程!看半天题目不知道咋写直接爆搜,结果分少的可怜,还不如直接贪搞个60分. 观察数据,发现图至多存在一个环. 显然,如果没有环,这个题不跟你多bb,直接贪就完事了,线性复杂度. ...
- 洛谷P5022 旅行 题解
前面几个代码都是部分分代码,最后一个才是AC了的,所以最后一个有详细注释 安利一发自己的Blog 这是提高组真题,233有点欧拉回路的感觉. 题目大意: 一个 连通 图,双向边 ,无重边 , 访问图中 ...
- Luogu P5022 旅行 搜索+贪心
好吧...一直咕..现在才过...被卡常卡到爆... 写的垃圾版本,$n^2$无脑删边..可以发现走出来的是棵树...更优秀的及数据加强版先咕着...一定写.qwq #include<cstdi ...
随机推荐
- Centos 安装PHP-redis扩展
从https://pecl.php.net/package/redis 里面找到自己安装的Redis对应版本的redis 1.获取已经安装的Redis版本扩展我这边安装的是4.0.1版本 wget ...
- Python之(scikit-learn)机器学习
一.机器学习(Machine Learning, ML)是一门多领域交叉学科,涉及概率论.统计学.逼近论.凸分析.算法复杂度理论等多门学科.专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或 ...
- 关于使用sublime的一些报错异常退出的解决方法
1.我用Sublime Text 3,装了一些插件,现在一打开软件就提示plugin_host has exited unexpectedly,如下图 解决方案 :
- 一种电平转换的方法,使用CPLD
参考应用笔记 http://www.doc88.com/p-0197252336968.html 前言 在原理图设计初期,可能涉及到引脚电平的转换操作,比如主FPGA的某BANK电平为1.5V,但外围 ...
- dubbo和mq的使用场景
MQ:消息队列.生产者消费者模式,可用于对消息实时性要求不高的场景.多进程之间间接调用关系 Dubbo:RPC实现.多进程之间直接调用关系 dubbo 1,rpc的分布式集群支持:负载均衡是对外提供一 ...
- py网络编程学习笔记
一.异常处理 异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下 而错误分为两种: 1 ...
- python urllib应用
urlopen 爬取网页 爬取网页 read() 读取内容 read() , readline() ,readlines() , fileno() , close() :这些方法的使用方式与文件对象完 ...
- Linux:入门基础
一.操作Linux必知必会基础知识 二.在Linux命令下查看命令帮助信息 三.Linux挂机重启注销命令 四.Linux显示系统IP地址 一.操作Linux必知必会基础知识 1.Linux命令行组成 ...
- 用js刷剑指offer(二叉搜索树与双向链表)
题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 牛客网链接 js代码 /* function TreeNode(x) { ...
- Java8新特性之重复注解(repeating annotations)
一.什么是重复注解 允许在同一申明类型(类,属性,或方法)的多次使用同一个注解 二.一个简单的例子java 8之前也有重复使用注解的解决方案,但可读性不是很好,比如下面的代码: 复制代码代码如下: p ...