【刷题】【环套树】旅行

noip2018

小 Y 的旅行方案是这样的:

任意选定一个城市作为起点,然后从起点开始,

每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市或者沿着第一次访问该 城市时经过的道路后退到上一个城市

当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。

 

需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。

为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。

她希望这个序列的字典序 最小

 

对于 100% 的数据和所有样例, 1n5000

 且 m = n − 1 或 m = n 。

 

题解:

因为只能回到上一个,所以当形状为树的时候,易知只能走dfs序

当形状为树上套一个环的时候,走法多样,无法简单便利,

所以我们考虑删除一条环上的边,用topo

很多预处理,很多细节,具体看代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
int n,m;
const int N=5003;
int head[N],tot;
struct node
{
    int v,nx;
}e[N<<1];
void add(int u,int v)
{
    e[++tot].v =v,e[tot].nx =head[u],head[u]=tot;
    e[++tot].v =u,e[tot].nx =head[v],head[v]=tot;
}

int in[N];
int son[N<<1],cnt,st[N],ed[N];
void topo()
{
    queue <int> q;
    for(int i=1;i<=n;i++)
        if(in[i]==1) q.push(i);
    while(!q.empty() )
    {
        int u=q.front() ;q.pop() ;
        for(int i=st[u];i<=ed[u];i++)
            if(in[son[i]]>1)
            {
                in[son[i]]--;
                if(in[son[i]]==1) q.push(son[i]); 
            }
    }    
}
bool broken[N][N],change,fail; 
int ans[N],pos;
void dfs(int rt,int f)
{
    if(fail) return ;
    
    ++pos;
    if(change || !ans[pos] || ans[pos]>rt ) 
        ans[pos]=rt,change=true;
    if( ans[pos]<rt ) 
    {
        fail=true;
        return ;
    }
    
    for(int i=st[rt];i<=ed[rt];i++)
        if(son[i]!=f && !broken[rt][son[i]] )
            dfs(son[i],rt);
}

void print()
{
    for(int i=1;i<=n;i++)
        printf("%d ",ans[i]);
    printf("\n");
}
int main()
{
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<=m;i++)
        scanf("%d%d",&u,&v),
        add(u,v);
    
    for(int i=1;i<=n;i++)//把son变成一段段排好序的序号,用于dfs 
    {
        st[i]=cnt+1;
        for(int j=head[i];j;j=e[j].nx )
            son[++cnt]=e[j].v ;
        ed[i]=cnt;
        sort(son+st[i],son+ed[i]+1);
    }
    if(m<n)
        pos=0,dfs(1,0),print();
    else
    {
        for(int i=1;i<=n;i++)
            in[i]=ed[i]-st[i]+1;
        topo();
        for(int i=1;i<tot;i+=2)
        {
            int u=e[i].v ,v=e[i+1].v ;
            if(in[u]==1 || in[v]==1) continue;
            
            broken[u][v]=broken[v][u]=true;
            fail=change=false;
            pos=0,dfs(1,0);
            broken[u][v]=broken[v][u]=false;
        }
        print();
    }
    
    return 0;
}

n^2的复杂度

https://www.cnblogs.com/mangoyang/p/9314823.html 

 

 

https://www.luogu.org/problem/P5021

https://www.luogu.org/problem/P4654

 

posted @ 2019-10-20 21:52  心若笺诗  阅读(112)  评论(0编辑  收藏  举报