5964. 【NOIP2018提高组D2T1】旅行

29 篇文章 13 订阅
19 篇文章 0 订阅

Description

小Y是一个爱好旅行的OIer。她来到X国,打算将各个城市都玩一遍。
小Y了解到,X国的n个城市之间有m条双向道路。每条双向道路连接两个城市。不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且,从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小Y只能通过这些道路从一个城市前往另一个城市。
小Y的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该城市时经过的道路后退到上一个城市。当小Y回到起点时,她可以选择结束这次旅行或继续旅行。需要注意的是,小Y要求在旅行方案中,每个城市都被访问到。
为了让自己的旅行更有意义,小Y决定在每到达一个新的城市(包括起点)时,将它的编号记录下来。她知道这样会形成一个长度为n的序列。她希望这个序列的字典序最小,你能帮帮她吗? 
对于两个长度均为n的序列A和B,当且仅当存在一个正整数x,满足以下条件时,我们说序列A的字典序小于B。
①对于任意正整数1<=i<x,序列A的第i 个元素Ai 和序列B的第i 个元素Bi相同。
②序列A的第x个元素的值小于序列B的第x个元素的值。

Input

输入文件名为travel.in。
输入文件共m+1行。第一行包含两个整数n,m(m<=n),中间用一个空格分隔。
接下来m行,每行包含两个整数u,v(1<=u,v<=n),表示编号为u和v的城市之间有一条道路,两个整数之间用一个空格分隔。

Output

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

Sample Input

输入1:
6 5
1 3
2 3
2 5
3 4
4 6

输入2:
6 6
1 3
2 3
2 5
3 4
4 5
4 6

Sample Output

输出1:
1 3 2 5 4 6

输出2:
1 3 2 4 5 6

Data Constraint

Source / Author: travel

Solution

1.注意到每经过一条新的边,必定访问了一个新的点。除了起点外,恰好访问了n-1个新的点,也就是整个过程只会经过n-1条边。这意味着经过的边形成了一棵生成树。若原图就是一棵树,我们考虑如何计算最优字典序。首先,我们一定选择 号点为起点。可以发现题目中的限制等 价于对原图的一次dfs,形成的序列就是dfs序。因此我们只需要确定访问孩子的顺序使得dfs序字典序最小,这显 然只需要按照编号从小到大访问孩子即可。算上排序,对树求答案的复杂度就是O(n long n)。

2.如果m=n,根据之前的结论一定有某一条边没有经过,可以枚举这是哪一条边,如果剩下的图是一棵树,就按照树的方法做一遍,然后更新答案。复杂度O(n^2 log n)。 不过我们并不需要每次都对节点排序,只需要开始时对每个节点的相邻节点排序即可,复杂度为O(n^2)。如果这样还是超时的话,其实可以不用找环,再删掉环上的边,我们可以每次在递归用栈统计答案的时候判断一下如果栈的当前这一位的前面所有的位置都不比答案大,且当前这一位比答案的这一位要大的话就可以直接推出。

 

Code1

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fd(i,a,b) for(int i=(a);i>=(b);--i)
#define bfo(i,v,u) for(int i=BB[v],u=B[i][1];i;u=B[i=B[i][0]][1])
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
int read()
{
	char ch;int n=0,p=1;
	for(ch=getchar();ch<'0' || '9'<ch;ch=getchar()) if(ch=='-') p=-1;
	for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';
	return n*p;
}
const int N=5005,M=N<<1;
struct edge
{
	int x,y,id;
	friend bool operator < (edge a,edge b) {return a.x!=b.x?a.x<b.x:a.y<b.y;}
}b[M];
int n,m,B0,BB[N],B[M][3];
void link(int u,int v,int id){B[++B0][1]=v,B[B0][2]=id,B[B0][0]=BB[u],BB[u]=B0;}
int cut;
bool bz[N];
void getans(int v)
{
	printf("%d ",v);
	bz[v]=1;
	bfo(i,v,u)
		if(!bz[u] && B[i][2]!=cut) getans(u);
}
bool vis[N],incir[M];
int stk[N];
bool findcir(int v,int fr=0)
{
	if(vis[v])
	{
		int i=stk[0]-1;
		while(i && B[stk[i]][1]!=v) i--;
		fo(j,i+1,stk[0]) incir[B[stk[j]][2]]=1;
		return 1;
	}
	vis[v]=1;
	bfo(i,v,u) if(u!=fr)
	{
		stk[++stk[0]]=i;
		if(findcir(u,v)) return 1;
		stk[stk[0]--]=0;
	}
	return 0;
}
void dfs(int v,int fr=0,int p=-1)
{
	if(vis[v]) return;
	vis[v]=1;
	int i=BB[v];
	if(B[i][1]==fr) i=B[i][0];
	for(int nxt=B[i][0],u=B[i][1];i;u=B[i=nxt][1],nxt=B[i][0])
	{
		if(B[nxt][1]==fr) nxt=B[nxt][0];
		if(!nxt)
		{
			if(p!=-1 && p<u && incir[B[i][2]]) cut=B[i][2];
			else dfs(u,v,p);
		}
		else
		dfs(u,v,B[nxt][1]);
		if(cut) return;
	}
}
int main()
{
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	n=read(),m=read();
	fo(i,1,m)
	{
		int x=read(),y=read();
		b[i+i]=(edge){x,y,i},b[i+i+1]=(edge){y,x,i};
	}
	sort(b+2,b+2*m+2);
	B0=1;
	for(int l=2,r=2;r<=2*m+1;l=r)
	{
		for(;b[r].x==b[l].x && r<=2*m+1;++r);
		fd(i,r-1,l) link(b[i].x,b[i].y,b[i].id);
	}
	if(m==n-1) return getans(1),0;
	findcir(1);
	mset(vis,0);
	dfs(1);
	getans(1);
	return 0;
}

Code2 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5010
using namespace std;
int n,m,x,y,t[N*2],nx[N*2],l[N],bz[N],ans[N],s[N],w[N][N],sh[N],b[N][N],h=0,bo;
void add(int x,int y){
	t[++t[0]]=y;
	nx[t[0]]=l[x];
	l[x]=t[0];
}
void dg(int x){
	if(!s[0]) return;
	for(int i=1;i<=w[x][0];i++){
		if(!bz[w[x][i]]&&!b[x][w[x][i]]){
			bz[w[x][i]]=1;
			s[++s[0]]=w[x][i];
			if(s[s[0]]<ans[s[0]]) bo=1;
			if(s[s[0]]>ans[s[0]]&&!bo){
				s[0]=0;return;
			}
			dg(w[x][i]);
		}
	}
}
int bj(){
	for(int i=1;i<=n;i++){
		if(s[i]<ans[i]) return 1;
		if(ans[i]<s[i]) return 0;
	}
	return 0;
}
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		w[x][++w[x][0]]=y;
		w[y][++w[y][0]]=x;
	}
	for(int i=1;i<=n;i++){
		sort(w[i]+1,w[i]+w[i][0]+1);
		ans[i]=n+2;
	}
	if(m==n-1){
		bz[1]=s[++s[0]]=1;
		dg(1);
		for(int i=1;i<=n;i++) printf("%d ",s[i]);
		return 0;
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=w[i][0];j++){
			b[i][w[i][j]]=b[w[i][j]][i]=1;
			memset(bz,0,sizeof(bz));
			bz[1]=s[s[0]=1]=bz[0]=1;
			bo=0;
			dg(1);
			bz[0]=1;
			for(int k=1;k<=n;k++){
				if(!bz[k]){
					bz[0]=0;
					break;
				}
			}
			if(bz[0]){
				if(bj()){for(int i=1;i<=n;i++) ans[i]=s[i];}
			}
			b[i][w[i][j]]=b[w[i][j]][i]=0;
		}
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/84111390

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值