问题标题: 酷町堂:阶段考试第3题

0
0
已解决
赵逸凡
赵逸凡
初级启示者
初级启示者
现在有一棵n个结点的树,在这棵树上又选取了两点,本来两点之间没有边,现在给它们之间增加了一条边。如果把这棵树看成是一个图,于是在这个图上产生了一个环。现在希望删掉一对点组成的边,使得这个图变回一棵树,请求出应该删掉哪一对点组成的边。
如果有多种删除方案,输出输入中最靠后的那一对边。(即树中产生了一个环,把环的最后一条输入的边给输出出来)

输入描述 Input Description
第一行,一个整数n,表示加了一条边后图中的边数
接下来n行,每行两个整数,a b,表示点a和点b之间存在一对边

输出描述 Output Description
两个空格隔开的整数,表示最后一个删掉之后变为树的那一对边,小的点在前,大的点在后

样例输入 Sample Input
3
1 2
1 3
2 3
样例输出 Sample Output
2 3

阶段考试的时候状态不好,导致这道水题20分,我先说下我的思路:

一开始的反应是欧拉路径,输出起点和终点等输入顺序靠后的结点编号。

然后第二种思路是按将输入顺序逆序删边(实时清空出度、入度),然后每删一条边就用欧拉回路判环,如果是环就输出2个点的编号,数据存储看n<=20就用邻接链表存储的,时间O(n²)左右,自己手造了几组数据、样例过了(老师说思路不对)

包括这个图:(哈密尔顿回路会卡的)

具体实现代码:

#include <iostream>
#include <cstring>
#define MAXN 1005
using namespace std;
int n,m,g[MAXN][MAXN],st,ed,s,u[MAXN],v[MAXN];
int path[MAXN],len,du[MAXN];
bool dfs(int x) 
{
    for(int i=1;i<=n;i++)
    {
        if(g[x][i])
        {
            g[x][i]=g[i][x]=0;
            dfs(i); 
        } 
    }
    path[++len]=x;
}
int main() 
{
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>u[i]>>v[i];
        du[u[i]]++;
        du[v[i]]++;
        g[u[i]][v[i]]=g[v[i]][u[i]]=1;
    }
    for(int i=n;i>=1;i--)
    {
        du[u[i]]--;
        du[v[i]]--;
        g[u[i]][v[i]]=g[v[i]][u[i]]=0;
        for(int j=1;j<=n;j++)
            if(du[j]%2==1)
            {
                s=j;
                break;
            }
        dfs(s);
        if(path[1]!=path[n])
        {
            cout<<min(v[i],u[i])<<" "<<max(u[i],v[i]);
            break;
        }
        s=0;
        len=0;
        memset(path,0,sizeof(path));
        du[u[i]]++;
        du[v[i]]++;
        g[u[i]][v[i]]=g[v[i]][u[i]]=1;
    }
}

大家帮我看下为什么WA 20分

大家的思路是什么?或者谁能给出卡掉我算法的数据?谢谢


0
已采纳
刘景程
刘景程
新手光能
新手光能

不是哈密尔顿啦亲

还有话说你的代码不是欧拉路的吗

你那个图无论删除哪个边都会有环

用并查集

刘景程在2020-08-12 14:27:13追加了内容

hack数据:

7
1 2
1 3
2 3
3 4
3 5
2 6
2 7

大概长这样的:

你的程序是欧拉路径

由于这个图无论删除哪条边,都不存在欧拉路,所以这东西就直接吐出了最后一条连线

刘景程在2020-08-12 14:31:15追加了内容

实际上应该输出

2 3

这才像树的亚子!

 

刘景程在2020-08-12 14:38:57追加了内容

截断边后的样子:

满足树的定义,即没有环的图。

第二组:

6
1 2
2 3
1 3
1 6
1 5
1 4

你程序的输出:1 4(原理如上)

正确答案:1 3

就不另外附图了哈亲

刘景程在2020-08-12 15:00:17追加了内容

讨论(废话)了这么多,讲讲正解

循环n条边,枚举可以删除的边

两点之间有连线的话,就unite(当然被删除的边不算)

如果这条被删除的边的两个端点有同样的代表,就意味着没有其他的回路(题目中说是一个树加上一条边,所以只可能有一个环),直接输出

记得初始化+倒序枚举

核心(不是代码!不要举报!)

for(倒序循环)
	{
		初始化并查集
		for(枚举另一个)
		{
			两条边不一样    unite
		}
		if(相同的代表)
		{
			输出(小的在前大的在后)
		}
	}

 

0
0
赵逸凡
赵逸凡
初级启示者
初级启示者

@王源松 @黄子扬 @陈喆鹏 @刘景程 

 

 

 

 

0
0
我要回答