P5022 旅行 (NOIP2018)

传送门

先考虑是一颗树的情况

求最小的 dfs 序

显然按儿子编号从小到大dfs

如果有多一条边怎么办

显然会有一条边不用走

直接枚举删那条边然后每次都暴力 dfs

复杂度 $O(n^2)$

注意每个节点的儿子顺序先预处理好

不要每次都重新算

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=10007;
int fir[N],from[N<<1],to[N<<1],cntt;//存排序前的边
inline void add(int &a,int &b)
{
    from[++cntt]=fir[a];
    fir[a]=cntt; to[cntt]=b;
}
int n,m,ans[N];
vector <int> v[N];//存排序后的边
int st[N],t,p1,p2;
bool vis[N];
void dfs(int x)//暴力dfs求字典序
{
    st[++t]=x; vis[x]=1;
    int len=v[x].size();
    for(int i=0;i<len;i++)
    {
        int &to=v[x][i]; if(vis[to]||(p1==x&&p2==to)||(p1==to&&p2==x)) continue;
        dfs(to);
    }
}
inline bool pd()//暴力比较字典序大小
{
    if(t<n) return 0;
    if(!ans[1]) return 1;
    for(int i=1;i<=n;i++)
        if(ans[i]!=st[i])
            return ans[i]<st[i] ? 0 : 1;
}
int e[N][2],tmp[N],tot;
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    int a,b;
    n=read(); m=read();
    for(int i=1;i<=m;i++)
    {
        a=read(); b=read();
        add(a,b); add(b,a);
        e[i][0]=a; e[i][1]=b;
    }
    for(int i=1;i<=n;i++)//预处理儿子顺序
    {
        tot=0;
        for(int j=fir[i];j;j=from[j]) tmp[++tot]=to[j];
        sort(tmp+1,tmp+tot+1);
        for(int j=1;j<=tot;j++) v[i].push_back(tmp[j]);
    }
    if(m==n-1)
    {
        dfs(1);
        for(int i=1;i<=n;i++) printf("%d ",st[i]);
        return 0;
    }
    for(int i=1;i<=m;i++)
    {
        t=0; p1=e[i][0]; p2=e[i][1];//暴力删边
        for(int i=1;i<=n;i++) vis[i]=0;
        dfs(1);
        if(pd()) for(int i=1;i<=n;i++) ans[i]=st[i];
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/10076260.html