题目链接

题意简介:现有一个图,小Y要把它走完,每个点只去一次,路径字典序最小。

分析:这道题我认为很重要的一个点就是它的数据范围。它只有两种 m=n-1 或 m=n。我们先考虑第一种:m=n-1也就是边为节点数减一,这种说法已经很隐晦了,其实这种情况就是树啊。树的遍历且字典序最小什么的应该很多人都会做吧,就只用深搜一下,这60分还是很好拿的。(由于每个点只做一次sort所以是不会超时的!)

void dfs_60(int u,int fa){
ans[++top]=u;
int ver[],k;
k=;
memset(ver,,sizeof(ver));
for(int i=head[u];i;i=next[i]){
if(to[i]!=fa)ver[++k]=to[i];
}
sort(ver+,ver+k+);
for(int i=;i<=k;++i){
dfs_60(ver[i],u);
}
return ;
}

然后我们要想个办法解决一个m=n的情况,并且在前面60的基础上加一些改动以达到ac的效果。那么m=n到底是个什么呢?他叫做:基环树。用我的理解来说,就是树里有一个环(也可以说是随便将一棵树上本不相连的两个点连接)。他大概长什么样子呢(m=n):

em,基本上是这个样子。其实啊,基环树这个东西,换句话说,只要删去环中的一条边也就变成了一棵树(多少颗不同的树取决于环的大小),那么我们可不可以删呢?在这道题当中是可以的。因为每一个点只去一次,就是说不存在从环上某个点走出去再沿着环返回这个点的这种情况,最多只走k-1条边(k为组成环的边数)。那么这道题我们从这k条边中每次取一条边出来删,按照m=n-1计算出最佳答案,最后所有的答案取最佳的就好。

那么目前只有一个问题:如何找环?

我是用的DFS,由于这道题只可能有一个环(因为m=n),所以就直接遍历,用一个栈存可能是环的点,遍历到已遍历的点的时候,就全部出栈直到那个点也弹出去。如果这个点所有子树搜完都没有找到环,那么把这个点出栈就行了。

void findh(int u,int fa){
if(flag==)return;
for(int i=head[u];i;i=next[i]){
if(to[i]!=fa){
if(vis[to[i]]){
while(to[i]!=st[top_st]){
h[++hsum]=st[top_st--];
}
h[++hsum]=to[i];
flag=;
}
else{
vis[to[i]]=;
st[++top_st]=to[i];
findh(to[i],u);
if(flag==)return;
top_st--;
vis[to[i]]=;
} }
}
}

接下来就任意删边用m=n-1弄就行了。注意一边搜索一边剪枝,不然会超时。

完整代码:

#include<iostream>
#include<cstdio>
#include<fstream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int read(){
char ch;
int res=,f=;
ch=getchar();
while(ch<''||ch>''){
if(ch=='-')f=-;
ch=getchar();
}
while(ch>=''&&ch<=''){
res=res*+(ch-'');
ch=getchar();
}
return res*f;
}
const int MAXN=;
int n,m,tot;
int head[MAXN],next[MAXN],to[MAXN];
int ans[MAXN],top;
int sz,sy,st[MAXN],top_st,vis[MAXN];
int h[MAXN],hsum,flag;
int lans[MAXN],ltop,flag_dfs;
void add(int x,int y){
to[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
bool check(int u,int v){
if(u==sz&&v==sy)return ;
if(u==sy&&v==sz)return ;
return ;
}
void dfs_60(int u,int fa){
ans[++top]=u;
if(flag_dfs==&&ans[top]<lans[top])flag_dfs=-;
if(flag_dfs==&&ans[top]>lans[top])flag_dfs=;
if(flag_dfs==)return;
if(flag_dfs==-&&top==n){
for(int i=;i<=top;++i){
lans[i]=ans[i];
}
flag_dfs=;
return;
}
int ver[],k=;
//memset(ver,0,sizeof(ver));
for(int i=head[u];i;i=next[i])if(to[i]!=fa&&check(u,to[i]))ver[++k]=to[i];
sort(ver+,ver+k+);
for(int i=;i<=k;++i)dfs_60(ver[i],u);
return ;
}
void findh(int u,int fa){
if(flag==)return;
for(int i=head[u];i;i=next[i]){
if(to[i]!=fa){
if(vis[to[i]]){
while(to[i]!=st[top_st]){
h[++hsum]=st[top_st--];
}
h[++hsum]=to[i];
flag=;
}
else{
vis[to[i]]=;
st[++top_st]=to[i];
findh(to[i],u);
if(flag==)return;
top_st--;
vis[to[i]]=;
} }
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;++i){
int u,v;
u=read();v=read();
add(u,v);add(v,u);
}
memset(lans,/,sizeof(lans));
if(n==m+){
dfs_60(,);
for(int i=;i<=top;++i){
printf("%d ",ans[i]);
}
}
else{
st[++top_st]=;
findh(,);
sz=h[],sy=h[hsum],top=;
dfs_60(,);
for(int i=;i<=hsum;++i){
sz=h[i-],sy=h[i],top=,flag_dfs=;
//memset(ans,0,sizeof(ans));
dfs_60(,);
}
}
for(int i=;i<=n;++i){
printf("%d ",lans[i]);
}
}
return ;
}

注:我注释掉了一些初始化的操作,是因为反正存答案的时候会覆盖,只用把top清零就行了

[NOIP 2018]旅行的更多相关文章

  1. NOIP 2018旅行题解

    从佳木斯回来刷一刷去年没A的题 题目描述 小 Y 是一个爱好旅行的 OIer.她来到 X 国,打算将各个城市都玩一遍. 小Y了解到, X国的 nn 个城市之间有 mm 条双向道路.每条双向道路连接两个 ...

  2. noip 2018 d2t1 旅行

    noip 2018 d2t1 旅行 (题目来自洛谷) 给定n个城市,m条双向道路的图, 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路.并且, 从任意一个城市出发,通过这些道路 ...

  3. [OI]Noip 2018总结(普及)

    考砸了,还有原谅我代码十分有限的可读性. 一个人的真正伟大之处就在于他能够认识到自己的渺小.——保罗 从一年前初一九月到现在18年10月接触OI已经有一年了.几次模拟赛也自我感觉良好,都过了一等的线, ...

  4. noip 2018 D1T3 赛道修建

    noip 2018 D1T3 赛道修建 首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数.考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径 ...

  5. NOIP 2018 总结

    NOIP 2018 总结 提高组: 应得分 \(100 + 100 + 40 + 100 + 50 + 44 = 434\). 考后期望得分 \(100 + 100 + 20 + 100 + 50 + ...

  6. NOIP 2018 真・退役记

    目录 NOIp 2018 真・退役记 7.01 7.05 \(summary\) 7.12 7.18 7.26 - 7.27 8.2 8.3 8.3 8.7 8.9 8.20 8.24 8.27 8. ...

  7. NOIP 2018 普及组 解题报告

    目录 标题统计 题目链接 思路 代码 龙虎斗 题目链接: 思路 代码 摆渡车 题目链接: 思路 对称二叉树 题目链接 思路: 先来解释一下为毛现在才来发解题报告: 其实博主是参加过NOIP 2018普 ...

  8. NOIp 2018 D2T1 旅行//未完成

    这个题没有认真读的话就会写下以下的DD代码 #include<bits/stdc++.h> #define N 5010 using namespace std; int n,m; int ...

  9. noip 2018 Day2 T1 旅行

    暴力删边,暴力枚举 #include <bits/stdc++.h> using namespace std; #define MAXM 5010 inline int read() { ...

随机推荐

  1. python基础学习(八)

    17.嵌套循环 # 嵌套循环 nested loop # 在一个循环中使用另外一个循环 num_list1 = [1, 2, 3, 4] num_list2 = [6, 7, 8, 9] # 组合li ...

  2. SQL语言(二)

    SQL约束与策略 create table student( id int primary key, //主键约束 name ) not null, //非空约束 idCard ) unique, / ...

  3. js复制内容到粘贴板

    点击右边内容:<span onclick="copyContent(this);" title="点击复制">啊,我被复制了</span> ...

  4. C#-System.Dynamic.ExpandoObject

    dynamic dynamicObject = new System.Dynamic.ExpandoObject(); dynamicObject.Id = Guid.NewGuid(); (dyna ...

  5. sqlserver-order by offset fetch

    若要使用 OFFSET 和 FETCH 在查询请求之间获得稳定的结果,必须满足以下条件: 查询使用的基础数据不能发生变化. 即,不会更新查询处理的行,也不会在单个事务中使用快照或可序列化事务隔离执行查 ...

  6. C# UTF-8文件带BOM和不带BOM文件的转换

    读取INI文件使用的是GetPrivateProfileString方法,自己读写ini文件没有问题. 调用C++的API对同一个ini文件进行处理后,发现首个Section的值读不出来:发现是API ...

  7. oracle 01741:非法的零长度标识

    转自:https://blog.csdn.net/wanderball/article/details/7690206 出现此问题是标识符里有两个连续的“”号,去掉即可,或是里面填充内容,避免两个连续 ...

  8. U-Boot补丁 S3C2440

    # tar xvf u-boot-1.1.6.tar.bz2 //解压 # cd u-boot-1.1.6/ 制作补丁文件 # diff -urN u-boot-1.1.6 u-boot-1.1.6. ...

  9. mysql limit和offset用法

    limit和offset用法 mysql里分页一般用limit来实现 1. select* from article LIMIT 1,3 2.select * from article LIMIT 3 ...

  10. 第六篇:Python函数进阶篇

    在了解完了 Python函数基础篇之后,本篇的存在其实是为了整合知识,由于该篇的知识是否杂乱,故大家可以通过点开点连接直接进入其详细介绍,该篇主要大致的介绍一下几个知识点:  一.Python的迭代器 ...