洛谷P5022&P5049 旅行(及其数据加强版)

旅行(不是加强版)
加强版



加强版数据范围:
我们注意到
也就是说要么是个树,要么是个基环树

60pts

这60分是个树,可以简单的贪心想到每次都走子树中编号最小的那个,并且把1作为根
dfs练手题
还是贴个代码叭

void dfs1(int now,int fa)
{
    if(vis[now])return ;
    ans1[++t]=now;
    vis[now]=1;
    vector<int> qwq;//为了不使存储的点被后面的子树覆盖,所以用vector
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        qwq.push_back(v);
    }
       sort(qwq.begin(),qwq.end());//vector的排序
    int qaq=qwq.size();
    for(int i=0;i<qaq;i++)
      dfs1(qwq[i],now);
}

100pts

大多数的解法

当m==n时,它是一个基环树(即树上挂着一个环的树)
基环树只要删掉环上的一条边,它就是个树了。所以我们可以枚举删掉哪条边。(需要开个\(O_2\)
由于开\(O_2\)会让你的评测记录显得不优雅,我们要考虑考虑怎么不开\(O_2\)过掉这道题。
由于我们要删去环上的边,所以要先找个环,而不是暴力删边再判是否是环上的。这样就可以过去了。

但是博主脑洞清奇所以并没有用这种做法当然也没有代码

当然不是
应某神仙的要求贴上他的代码

oid dfs3(int from,int fa) {//找环代码
    vis[from]=1;
    for(int i=0;i<a[from].size();i++) {//这里是用vector记录的出边
        int to=a[from][i];
        if(to==fa)
            continue ;
        if(vis[to]) {
            flag=1;//找到了环
            cir1[to]=1;//标记to和from都在环上
            cir1[from]=1;
            u1[++cnt]=from;//记录在环上的点
            v1[cnt]=to;
            return ;
        }
        dfs3(to,from);
        if(flag&&cir1[to])
            if(cir1[from]) {//判断找到了环的“根”(即以1为根时,最靠近1的在环上的点)
                flag=0;
                u1[++cnt]=from;
                v1[cnt]=to;
                return ;
            } else {
                cir1[from]=1;
                u1[++cnt]=from;
                v1[cnt]=to;
                return ;
            }
    }
}

全套代码

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> a[5010];
int n,m;
int res[5010],ans[5010],tot;
int cir1[5010];
int u1[10010],v1[10010],cnt;
int vis[5010];
int du,dv;
int flag;
struct Edge {//咱也不造为啥铁锤妹妹要写前向星(虽然后面也没有用到前向星)
    int from,to;
}e[5010];
void dfs2(int u,int fa) {//没有环时的dfs
    if(vis[u])
        return ;
    vis[u]=1;
    ans[++tot]=u;//记录答案
    for(int i=0;i<a[u].size();i++) {
        int v=a[u][i];
        if(v==fa)
            continue ;
        dfs2(v,u);
    }
}
void dfs1(int u,int fa) {//有环时的dfs
    if(vis[u])
        return ;
    vis[u]=1;
    res[++tot]=u;
    for(int i=0;i<a[u].size();i++) {
        int v=a[u][i];
        if(v==fa)
            continue ;
        if((u==du&&v==dv)||(u==dv&&v==du))//du,dv为枚举删去的边(见主函数)
            continue ;
        dfs1(v,u);
    }
}
void dfs3(int from,int fa) {//找环
    vis[from]=1;
    for(int i=0;i<a[from].size();i++) {
        int to=a[from][i];
        if(to==fa)
            continue ;
        if(vis[to]) {
            flag=1;
            cir1[to]=1;
            cir1[from]=1;
            u1[++cnt]=from;
            v1[cnt]=to;
            return ;
        }
        dfs3(to,from);
        if(flag&&cir1[to])
            if(cir1[from]) {
                flag=0;
                u1[++cnt]=from;
                v1[cnt]=to;
                return ;
            } else {
                cir1[from]=1;
                u1[++cnt]=from;
                v1[cnt]=to;
                return ;
            }
    }
}
int check() {//比较更优方案
    for(int i=1;i<=n;i++) {
        if(res[i]<ans[i])
            return 1;
        else if(res[i]>ans[i])
            return 0;
    }
    return 0;
}
void update() {//更新答案
    for(int i=1;i<=n;i++) {
        ans[i]=res[i];
    }
}
int main() {
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&u,&v);
        a[u].push_back(v);//vector选手铁锤妹妹
        a[v].push_back(u);
        e[i].from=u;
        e[i].to=v;
    }
    for(int i=1;i<=n;i++)
        sort(a[i].begin(),a[i].end());
    if(m==n) {
        dfs3(1,0);//找环
        int flag=1;
        for(int i=1;i<=cnt;i++) {
            du=u1[i];dv=v1[i];//枚举删去环上哪条边
            memset(vis,0,sizeof(vis));
            tot=0;
            dfs1(1,0);
            if(tot<n)
                continue ;
            if(flag) {
                update();
                flag=0;
            }
            if(check())
                update();
        }
        for(int i=1;i<=n;i++) {
            printf("%d ",ans[i]);
        }
    } else {
        dfs2(1,0);
        for(int i=1;i<=n;i++) {
            printf("%d ",ans[i]);
        }
    }
    return 0;
}

代码转自铁锤妹妹,注释窝加的
接下来我们谈谈博主清奇的脑洞。
考虑从环入手选择最优解。
先来看最简单的环。

最优解当然是1 2 3 4 5辣。那我们究竟是怎么找出这个顺序的呢?
首先按照60pts的思路,走编号最小的点。走到2。下一个是5,但是如果我们此时回溯到3,走3-->4-->5这条路,所得的字典序会更小。由此可以得到一个贪心思路:在向编号小的点a走的同时,记录下编号较大的点b的编号。当dfs到一个比b编号大且在环上的点时,回溯到b,由b走过去。
我们在记录参数(b的编号)(以下称之为cs)的时候,是在环的“根”处(也就是图中的1节点)记录的,所以要先找个环并且记录环的“根”。
现在把这个环挂到树上。

最优解是1 2 6 4 3 5 7。我们发现在遍历2的子树时一定要走过7.此时就无法回到3然后从3走了。因此我们对cs要有所改变。(cs初始化为inf)
cs更新原则:
如果当前点now是环的“根”root,则cs为它的子树中,在环上且编号较大的那个点
如果当前点在环上但不是root,且cs不是inf。记录它在环上的子树的编号c,找到最大的不在环上的且大于c的子树编号。如果没有,则cs不变,如果有,cs更新。
为什么要cs不是inf才能更新呢?因为如果cs是inf且在环上,说明现在是从编号较大的点走过来的,不需要再判断是否回溯。
找环+对环的dfs:

int rt,er;//rt就是上文中的root,er记录在找环时是否回溯到了root
void huan(int now,int fa)
{
    vis[now]++;
    if(vis[now]>1)
    {hua[now]=1;rt=now;er++;return ;}
    if(!head[now])return ;
    bool bj=0;//记录是否有子树在环上
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        if(now==rt)break;
        huan(v,now);
        if(hua[v])bj=1;
    }
    if(bj)hua[now]=1;//如果有子树在环上,那么now很可能也在环上,特殊情况由下面判断
    if(er==2)hua[now]=0;//er==2说明已经回到了root的父亲节点(祖先节点)
    if(now==rt)er++;
}
void dfs2(int now,int fa,int cs)
{
        if(now>cs)return ;//该回溯了
    if(vis[now])return ;
    vis[now]=1;
    vector<int> qwq;
    ans1[++t]=now;//记录答案
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        qwq.push_back(v);
    }
    sort(qwq.begin(),qwq.end());//依旧是排序觉得子树的顺序
    int qaq=qwq.size();
    if(now==rt)//在root处开始记录cs
     for(int i=qaq-1;i>=0;i--)
          if(hua[qwq[i]]){cs=qwq[i];break;}  
        if(now!=rt&&hua[now]&&cs!=inf)
        {
          int rwr=inf;
          for(int i=qaq-1;i>=0;i--)
            if(hua[qwq[i]]){rwr=qwq[i];break;}//rwr记录子树中在环上的点的编号
      for(int i=qaq-1;i>=0;i--)
        if(!hua[qwq[i]]&&qwq[i]>rwr){cs=qwq[i];break;}
    }
    for(int i=0;i<qaq;i++)
       {
                if((qwq[i]<cs&&now==rt)||(now!=rt&&hua[qwq[i]])) dfs2(qwq[i],now,cs);//如果是开始走编号较小的点或者说now在环上则要带着cs(由于走较大的点的编号的情况在下面更新了cs,所以这么写也是可以的)
        else dfs2(qwq[i],now,inf);//在走较大的点的时候把cs更新掉
    }
}

完整版(无注释):

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
inline int read()
{
    char ch=getchar();
    int x=0;bool f=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();       
    }
    return f?-x:x;
}
const int inf=214748364;
int t,n,m,ans1[500009],cnt,head[500009];
int vis[500009];
bool hua[500009];
struct E{
    int to,nxt;
}ed[1000009];
void add(int fr,int to)
{
    ed[++cnt].to=to;
    ed[cnt].nxt=head[fr];
    head[fr]=cnt;
}
void dfs1(int now,int fa)
{
    if(vis[now])return ;
    ans1[++t]=now;
    vis[now]=1;
    vector<int> qwq;
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        qwq.push_back(v);
    }
    sort(qwq.begin(),qwq.end());
    int qaq=qwq.size();
    for(int i=0;i<qaq;i++)
      dfs1(qwq[i],now);
}
int rt,er;
void huan(int now,int fa)
{
    vis[now]++;
    if(vis[now]>1)
    {hua[now]=1;rt=now;er++;return ;}
    if(!head[now])return ;
    bool bj=0;
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        if(now==rt)break;
        huan(v,now);
        if(hua[v])bj=1;
    }
    if(bj)hua[now]=1;
    if(er==2)hua[now]=0;
    if(now==rt)er++;
}
void dfs2(int now,int fa,int cs)
{
    if(now>cs)return ;
    if(vis[now])return ;
    vis[now]=1;
    vector<int> qwq;
    ans1[++t]=now;
    for(int e=head[now];e;e=ed[e].nxt)
    {
        int v=ed[e].to;
        if(v==fa)continue;
        qwq.push_back(v);
    }
    sort(qwq.begin(),qwq.end());
    int qaq=qwq.size();
    if(now==rt)
     for(int i=qaq-1;i>=0;i--)
        if(hua[qwq[i]]){cs=qwq[i];break;}    
    if(now!=rt&&hua[now]&&cs!=inf)
    {
        int rwr=inf;
        for(int i=qaq-1;i>=0;i--)
         if(hua[qwq[i]]){rwr=qwq[i];break;}
        for(int i=qaq-1;i>=0;i--)
         if(!hua[qwq[i]]&&qwq[i]>rwr){cs=qwq[i];break;}
    }
    for(int i=0;i<qaq;i++)
    {
        if((qwq[i]<cs&&now==rt)||(now!=rt&&hua[qwq[i]])) dfs2(qwq[i],now,cs);
        else dfs2(qwq[i],now,inf);
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int fr=read(),to=read();
        add(fr,to);
        add(to,fr);
    }
    if(m==n-1)dfs1(1,0);
    else 
    {
        huan(1,0);
        memset(vis,0,sizeof(vis));
        dfs2(1,0,inf);
    }
    for(int i=1;i<=t;i++)
     printf("%d ",ans1[i]);
    memset(vis,0,sizeof(vis)); 
}

接下来就是愉快的AC了
然后博主发现自己的做法好像比较清奇,于是去交了数据加强版,发现也A了
加强版:把数组改大然后交上就ok了

猜你喜欢

转载自www.cnblogs.com/lcez56jsy/p/11767842.html