[NOIP 2018]旅行
题意简介:现有一个图,小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]旅行的更多相关文章
- NOIP 2018旅行题解
从佳木斯回来刷一刷去年没A的题 题目描述 小 Y 是一个爱好旅行的 OIer.她来到 X 国,打算将各个城市都玩一遍. 小Y了解到, X国的 nn 个城市之间有 mm 条双向道路.每条双向道路连接两个 ...
- noip 2018 d2t1 旅行
noip 2018 d2t1 旅行 (题目来自洛谷) 给定n个城市,m条双向道路的图, 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路.并且, 从任意一个城市出发,通过这些道路 ...
- [OI]Noip 2018总结(普及)
考砸了,还有原谅我代码十分有限的可读性. 一个人的真正伟大之处就在于他能够认识到自己的渺小.——保罗 从一年前初一九月到现在18年10月接触OI已经有一年了.几次模拟赛也自我感觉良好,都过了一等的线, ...
- noip 2018 D1T3 赛道修建
noip 2018 D1T3 赛道修建 首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数.考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径 ...
- NOIP 2018 总结
NOIP 2018 总结 提高组: 应得分 \(100 + 100 + 40 + 100 + 50 + 44 = 434\). 考后期望得分 \(100 + 100 + 20 + 100 + 50 + ...
- 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. ...
- NOIP 2018 普及组 解题报告
目录 标题统计 题目链接 思路 代码 龙虎斗 题目链接: 思路 代码 摆渡车 题目链接: 思路 对称二叉树 题目链接 思路: 先来解释一下为毛现在才来发解题报告: 其实博主是参加过NOIP 2018普 ...
- NOIp 2018 D2T1 旅行//未完成
这个题没有认真读的话就会写下以下的DD代码 #include<bits/stdc++.h> #define N 5010 using namespace std; int n,m; int ...
- noip 2018 Day2 T1 旅行
暴力删边,暴力枚举 #include <bits/stdc++.h> using namespace std; #define MAXM 5010 inline int read() { ...
随机推荐
- Springmvc在项目启动时查询数据库并初始化静态变量
private static List<ResourceEntity> resourceList = null; //初始化的全局静态变量 @Autowired private Resou ...
- 使用TypeScript创建Vue项目
Vue的灵活性总是让代码看起来非常洗练,对TypeScript来说也是一种挑战, 好在Vue对TypeScript进行了一次全方位的适配. 相对于React严谨的代码,Redux啰嗦的样板代码,Vue ...
- python基础学习(七)
14.return # print() 可以被执行 def doubelNumber(num): print() print() Afnum = doubelNumber() print(Afnum) ...
- python 之 数据库(创建表的完整语法、基本数据类型)
10.4 创建表的完整语法 create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) 约束条件] ); #类型:使用限 ...
- 函数的第一类对象,f格式化,迭代器以及递归
函数名的第一类对象及使用,f格式化以及迭代器 1.函数的第一类对象 第一类对象 --特殊点 1.可以当作值被赋值给变量 def func(): print(1) a = func a() 2.可以当作 ...
- 机器学习-EM算法笔记
EM算法也称期望最大化(Expectation-Maximum,简称EM)算法,它是一个基础算法,是很多机器学习领域算法的基础,比如隐式马尔科夫算法(HMM), LDA主题模型的变分推断,混合高斯模型 ...
- 机器学习之逻辑回归(Logistic)笔记
在说逻辑回归之前,可以先说一说逻辑回归与线性回归的区别: 逻辑回归与线性回归在学习规则形式上是完全一致的,它们的区别在于hθ(x(i))为什么样的函数 当hθ(x(i))=θTx(i)时,表示的是线性 ...
- C++动态内存常见面试题解析
malloc/free和new/delete傻傻分不清?动态内存管理的面试题难道你了?来看这篇文章,包你全会. 1.malloc/free和new/delete的区别 (1)mall ...
- 手动实现自己的spring事务注解
spring事务是基于同一个数据连接来实现的,认识到这一点是spring事务的关键,spring事务的关键点便在于在事务中不管执行几次db操作,始终使用的是同一个数据库连接.通过查看源码,我们可以看到 ...
- ScrollView小记
常用代理方法: - (void)scrollViewDidScroll:(UIScrollView *)scrollView 只有 [self.scrolView setContentOffset: ...