NOIp提高组2018 旅行————基环树+搜索

题解:本题主要考查基环树+搜索。
简要题意: n n n个城市之间有 m m m条双向道路,小Y每到达一个新的城市(包括起点)时,将它的编号记录下来。形成一个长度为 n n n的序列。求这个序列的字典序最小。
对于 100 % 100\% 100%的数据和所有样例, 1 ≤ n ≤ 5000 1≤n≤5000 1n5000 m = n − 1 m=n−1 m=n1 m = n m=n m=n
1.对于 60 % 60\% 60%的数据:我们注意到 m = n − 1 m=n-1 m=n1,也就是说这是棵树。那我们可以每次都挑选编号较小的子树进行遍历即可。
代码如下:

#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
vector<int> g[666666];
int v[666666],ans[666666];
int n,m,P,num;
void dfs(int p,int fa)
{
	if(v[p])return ;
	ans[++num]=p;
	v[p]=1;
	for(int i=0;i<g[p].size();i++)
	{
		int k=g[p][i];
		if(k==fa)continue;
	    dfs(k,p);
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	sort(g[i].begin(),g[i].end());
	dfs(1,0); 
	for(int i=1;i<=num;i++)
	cout<<ans[i]<<" "; 
	return 0;
} 

2.对于 100 % 100\% 100%的数据: m = n m=n m=n是一棵基环树,对于基环树,决策的难点在于环上的部分。我们就删边+DFS,即可。
代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int>g[666666];
struct edge
{
    int u,to,start;
}e[666666];
int head[666666],vis[666666],ans[666666],k[666666];
int n,m,num,x,y,P;
int read()
{
	int flag=1,sum=0;
    char c;
    scanf("%c",&c);
    while (c<'0'||c>'9')
    {
        if(c=='-')flag=-1;
        scanf("%c",&c);
    }
    while (c>='0'&&c<='9')
    {
        sum=sum*10+c-'0';
        scanf("%c",&c);
    }
    return sum*flag;
}
void add(int start,int to)
{
	e[++P].u=start;
    e[P].to=to;
    e[P].start=head[start];
    head[start]=P;
}
void dfs(int p,int fa)
{
    if(vis[p])return;
    vis[p]=1;
    k[++num]=p;
    for(int i=0;i<g[p].size();i++)
	{
        int k=g[p][i];
        if(k==fa)continue;
        if((k==y&&p==x)||(k==x&&p==y))continue;
        dfs(k,p);
    }
}
bool check()
{
    for(int i=1;i<=n;i++)
	{
        if(k[i]==ans[i])continue;
        if(k[i]>ans[i])return 0;
        else return 1;
    }
}
void change()
{
    for(int i=1;i<=n;i++)ans[i]=k[i];
}
void dfs2(int p,int fa)
{
    if(vis[p])return;
    vis[p]=1;
    ans[++num]=p;
    for(int i=0;i<g[p].size();i++)
	{
        int k=g[p][i];
        if(k==fa)continue;
        dfs2(k,p);
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
	{
		int u,v;
        u=read();v=read();
        g[u].push_back(v);
        g[v].push_back(u);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
    sort(g[i].begin(),g[i].end());
    if(n==m)
	{
        for(int i=0;i<P;i+=2)
		{
            num=0;x=e[i].u;y=e[i].to;
            memset(vis,0,sizeof(vis));
            dfs(1,0);
            if(num<n)continue;
            if(ans[1]==0)change();
            else if(check())change();
        }                    
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
        return 0;
    }
    dfs2(1,0);
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值