zoukankan      html  css  js  c++  java
  • 洛谷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了

  • 相关阅读:
    百度之星资格赛1001——找规律——大搬家
    HDU1025——LIS——Constructing Roads In JGShining's Kingdom
    DP(递归打印路径) UVA 662 Fast Food
    递推DP UVA 607 Scheduling Lectures
    递推DP UVA 590 Always on the run
    递推DP UVA 473 Raucous Rockers
    博弈 HDOJ 4371 Alice and Bob
    DFS(深度) hihoCoder挑战赛14 B 赛车
    Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2)
    DP(DAG) UVA 437 The Tower of Babylon
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/11767842.html
Copyright © 2011-2022 走看看