洛谷P5022 [NOIP2018 提高组] 旅行 题解

题目

题目背景

NOIP2018 提高组 D2T1

题目描述

小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。 
  
小 Y 了解到,X 国的 n 个城市之间有 m 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。   

小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。   

为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 n 的序列。她希望这个序列的字典序 最小,你能帮帮她吗?  对于两个长度均为 n 的序列 A 和 B,当且仅当存在一个正整数 x,满足以下条件时, 我们说序列 A 的字典序小于 B。 
  
- 对于任意正整数 1 ≤ i < x,序列 A 的第 i 个元素 A_i 和序列 B 的第 i 个元素 B_i 相同。   
- 序列 A 的第 x 个元素的值小于序列 B 的第 x 个元素的值。

输入格式

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

输出格式

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

样例 #1

样例输入 #1

6 5 
1 3 
2 3 
2 5 
3 4 
4 6

样例输出 #1

1 3 2 5 4 6

样例 #2

样例输入 #2

6 6 
1 3 
2 3 
2 5 
3 4 
4 5 
4 6

样例输出 #2

1 3 2 4 5 6

提示

【数据规模与约定】   

对于 100% 的数据和所有样例,1≤n≤5000 且 m=n−1 或 m=n 。

对于不同的测试点, 我们约定数据的规模如下:

分析

当m=n-1时,这个图就是一个树;当m=n时,这个图就是只有1个环的图

所以当m=n-1时,我们就可以直接用深搜;当m=n时,我们就可以遍历这n条边,每次把其中一条边删去后再用m=n-1时的办法搜

讲解

1.变量定义

int n,m,flag[5005],b[5005],ans[5005],cnt,ok;
vector<int>g[5005];
struct node
{
	int u,v;
}edge[10005];

n,m为输入数据,结构体edge用来存边,vector用来边排序,ans数组存最终答案

2.主函数

int main()
{
 	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
		edge[i].u=u;edge[i].v=v;
	}
	for(i=1;i<=n;i++)
	{
		sort(g[i].begin(),g[i].end());
	}
	if(m==n-1)
	{
		dfs1(1);
	}
	else
	{
		memset(ans,0x3f,sizeof(ans));
		for(i=1;i<=n;i++)
		{
			cnt=0;memset(b,0,sizeof(b));memset(flag,0,sizeof(flag));ok=0;
			dfs2(1,edge[i].u,edge[i].v);
			if(cnt==n&&f1(b,ans))
			{
				for(j=1;j<=n;j++)
				{
					ans[j]=b[j];
				}
			}
		}
		pt();
	}
 	return 0;
}

先输入,把边存上,再进行边排序

如果m=n-1,直接用深搜

如果m=n,则遍历这n条边,每次把其中一条边删去后再用m=n-1时的办法搜,搜出来的结果存到b数组里,再和ans数组比较,如果b数组字典序更小,就更新ans数组,别忘了每次搜的时候b和flag数组要初始化,ans数组要赋值为极大值

3.dfs1

void dfs1(int s)
{
	flag[s]=1,ans[++cnt]=s;
	if(cnt==n)
	{
		pt();ok=1;return;
	}
	if(ok==1) return;
	for(auto i:g[s])
	{
		if(flag[i]==0) dfs1(i);
	}
}

这是一个很简单的深搜,每次从当前点去搜下一个点,但前提是不要走回头路,即下一个点没有被搜过

如果已经搜完了这n个数,就直接输出

4.dfs2

void dfs2(int s,int x,int y)
{
	flag[s]=1,b[++cnt]=s;
	if(cnt==n)
	{
		ok=1;return;
	}
	if(ok==1) return;
	for(auto i:g[s])
	{
		if(flag[i]==0&&pd(s,i,x,y)) dfs2(i,x,y);
	}
}

dfs2其实和dfs1差不多,只不过就是多了判断当前点和下一个点之间的边是不是删去的那条边

pd(s,i,x,y)就是判断s和i之间的边是不是删去的边

完整AC代码

#include <bits/stdc++.h>
using namespace std;
int n,m,flag[5005],b[5005],ans[5005],cnt,ok;
vector<int>g[5005];
struct node
{
	int u,v;
}edge[10005];
int pd(int a,int b,int c,int d)
{
	if(a==c&&b==d) return 0;
	if(a==d&&b==c) return 0;
	return 1;
}
void pt()
{
	for(int i=1;i<=n;i++)
	{
		printf("%d ",ans[i]);
	}
}
void dfs1(int s)
{
	flag[s]=1,ans[++cnt]=s;
	if(cnt==n)
	{
		pt();ok=1;return;
	}
	if(ok==1) return;
	for(auto i:g[s])
	{
		if(flag[i]==0) dfs1(i);
	}
}
void dfs2(int s,int x,int y)
{
	flag[s]=1,b[++cnt]=s;
	if(cnt==n)
	{
		ok=1;return;
	}
	if(ok==1) return;
	for(auto i:g[s])
	{
		if(flag[i]==0&&pd(s,i,x,y)) dfs2(i,x,y);
	}
}
int f1(int a[],int b[])
{
	for(int i=1;i<=n;i++)
	{
		if(a[i]<b[i]) return 1;
		if(a[i]>b[i]) return 0;
	}
	return 0;
}

int main()
{
 	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
		edge[i].u=u;edge[i].v=v;
	}
	for(i=1;i<=n;i++)
	{
		sort(g[i].begin(),g[i].end());
	}
	if(m==n-1)
	{
		dfs1(1);
	}
	else
	{
		memset(ans,0x3f,sizeof(ans));
		for(i=1;i<=n;i++)
		{
			cnt=0;memset(b,0,sizeof(b));memset(flag,0,sizeof(flag));ok=0;
			dfs2(1,edge[i].u,edge[i].v);
			if(cnt==n&&f1(b,ans))
			{
				for(j=1;j<=n;j++)
				{
					ans[j]=b[j];
				}
			}
		}
		pt();
	}
 	return 0;
}

以上就是这道题题解的全部内容

  • 34
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值