藍橋杯訓練——[藍橋杯][2017年第八屆真題]發現環

題目鏈接:https://www.dotcpp.com/oj/problem1841.html

題目描述:
小明的實驗室有N臺電腦,編號1~N。原本這N臺電腦之間有N-1條數據鏈接相連,恰好構成一個樹形網絡。在樹形網絡上,任意兩臺電腦之間有唯一的路徑相連。
不過在最近一次維護網絡時,管理員誤操作使得某兩臺電腦之間增加了一條數據鏈接,於是網絡中出現了環路。環路上的電腦由於兩兩之間不再是隻有一條路徑,使得這些電腦上的數據傳輸出現了BUG。
爲了恢復正常傳輸。小明需要找到所有在環路上的電腦,你能幫助他嗎?

輸入描述:
第一行包含一個整數N。
以下N行每行兩個整數a和b,表示a和b之間有一條數據鏈接相連。
對於30%的數據,1 <= N <= 1000
對於100%的數據, 1 <= N <= 100000, 1 <= a, b <= N
輸入保證合法。

輸出描述:
按從小到大的順序輸出在環路上的電腦的編號,中間由一個空格分隔。

輸入樣例:
5
1 2
3 1
2 4
2 5
5 3

輸出樣例:
1 2 3 5

 此題兩種解法:(1)先用並查集找出環路中的相鄰兩點,再用搜索(bfs)找出兩點間的路徑。

                          (2)拓撲排序,只將度爲1 的點入隊,最終沒有進隊列的點即爲環路節點

並查集+bfs:

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <cstdio>
#include <string>
#include <stack>
#include <set>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
using namespace std;
typedef long long ll;
//鏈式前向星邊信息
struct node
{
    int y,next_s;/* data */
}a[200010];
//並查集邊信息
struct node1
{
    int x,y;/* data */
}c[100010];
int head[100010];//head[x]以x爲起點的某條邊的編號
int cnt;//邊號計數
int n;//
int f[100010];//並查集祖先數組
bool vis[100010];//訪問數組
int pre[100010];//pre[x]=y指向直接父節點(x的直接父節點是y)
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void add(int x,int y){
    a[cnt].y=y;
    a[cnt].next_s=head[x];
    head[x]=cnt++;
}
int find(int x){
    int r=x;
    while(r!=f[r])r=f[r];
    int j=x;
    while(f[j]!=r){
        f[j]=r;
        j=f[x];
        x=j;
    }
    return r;
}
void bfs(int st,int en,int &ans){
    queue<int > p;
    int book=0;
    //先判斷是否st和en之間存在兩條邊,如果存在,不需要bfs搜索
    for(int i=head[st];i!=-1;i=a[i].next_s){
        int v=a[i].y;
        if(v==en){
            book++;
        }
        else {
            vis[v]=true;
            pre[v]=st;
            p.push(v);
        }
    }
    if(book==2){
        ans=2;
        memset(vis,false,sizeof(false));
        vis[st]=vis[en]=true;
        return ;
    }
    vis[st]=true;
    //排除了st,en自成環的可能
    while(!p.empty()&&!vis[en]){
        int t=p.front();
        p.pop();
        for(int i=head[t];i!=-1&&!vis[en];i=a[i].next_s){
            int v=a[i].y;
            if(vis[v])continue;
            pre[v]=t;//用於回溯標記
            vis[v]=true;
            p.push(v);
        }
    }
    while(!p.empty())p.pop();
    memset(vis,false,sizeof(vis));
    vis[st]=true;
    ans=1;
    //利用pre數組從en回溯查找到st,並且標記
    for(int i=en;i!=st;i=pre[i]){
        vis[i]=true;
        ans++;
    }
}

int main()
{
    IOS;
    init();
    int x,y;
    cin>>n;
    for(int i=1;i<=n;i++){
        f[i]=i;
    }
    for(int i=0;i<n;i++){
        cin>>x>>y;
        c[i].x=x;
        c[i].y=y;
        add(x,y);//無向圖建立兩條有向邊
        add(y,x);
    }
    int st,en;
    for(int i=0;i<n;i++){
        int tx=find(c[i].x);
        int ty=find(c[i].y);
        if(tx==ty){//找到迴路中的兩個點
            st=c[i].x;
            en=c[i].y;
            break;
        }
        //養成習慣:讓小根作爲根節點
        if(tx>ty){
            f[tx]=ty;
        }
        else f[ty]=tx;
    }
    int ans=0;//統計環上節點個數
    bfs(st,en,ans);
    //按順序輸出
    for(int i=1;i<=n;i++){
        if(vis[i]){
            cout<<i;
            if(ans>1)cout<<" ";
            else cout<<endl;
            ans--;
        }
    }
    getchar();
    getchar();
    return 0;
}

 拓撲排序:

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <cstdio>
#include <string>
#include <stack>
#include <set>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
using namespace std;
typedef long long ll;
struct node_1
{
    int y,next_s;/* data */
}a[200010];
int head[100010];
int cnt=0;
int du[100010];
bool del[100010];//del[i]=true,i節點被刪除
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void add(int x,int y){
    a[cnt].y=y;
    a[cnt].next_s=head[x];
    head[x]=cnt++;
}
int main()
{
	IOS;
	int n,x,y;
	init();
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		add(x,y);
		add(y,x);
		du[x]++;
		du[y]++;
	}
	queue<int > p;
	int del_cnt=0;
	//刪除度爲1的節點
	for(int i=1;i<=n;i++){
		if(du[i]==1){
			del_cnt++;
			del[i]=true;
			p.push(i);
		}
	}
	while(!p.empty()){
		int t=p.front();
		p.pop();
		for(int i=head[t];i!=-1;i=a[i].next_s){
			int v=a[i].y;
			if(del[v])continue;//已經刪除
			du[v]--;//相鄰接點度減一
			if(du[v]<=1){//判斷是否要刪除
				del[v]=true;
				del_cnt++;
				p.push(v);
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(del[i])continue;
		if(++del_cnt==n)cout<<i<<endl;
		else cout<<i<<" ";
	}
	getchar();
	getchar();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章