PTA 7-32 哥尼斯堡的“七橋問題”(歐拉回路)

本題考查點:

  • 歐拉回路 [TOC]

哥尼斯堡是位於普累格河上的一座城市,它包含兩個島嶼及連接它們的七座橋,如下圖所示。 可否走過這樣的七座橋,而且每橋只走過一次?瑞士數學家歐拉(Leonhard Euler,1707—1783)最終解決了這個問題,並由此創立了拓撲學。 這個問題如今可以描述爲判斷歐拉回路是否存在的問題。歐拉回路是指不令筆離開紙面,可畫過圖中每條邊僅一次,且可以回到起點的一條迴路。現給定一個無向圖,問是否存在歐拉回路? 輸入格式: 輸入第一行給出兩個正整數,分別是節點數N (1≤N≤1000)和邊數M;隨後的M行對應M條邊,每行給出一對正整數,分別是該條邊直接連通的兩個節點的編號(節點從1到N編號)。 輸出格式: 若歐拉回路存在則輸出1,否則輸出0。

在做本題之前,我們先來複習一下歐拉圖,歐拉回路,歐拉通路的一些性質:

歐拉圖、歐拉通路與歐拉回路

歐拉圖的定義

  • 設 G 是無孤立結點的圖,若存在一條通路 (迴路),經過圖中每邊一次且僅一次,則稱 此通路 (迴路) 爲該圖的一條歐拉通路 (迴路)。具有歐拉回路的圖稱爲歐拉圖(Eulerian graph)

無向歐拉圖判定定理

  • 歐拉回路:無向圖 G =< V, E > 具有一條歐拉回路,當且僅當 G 是連通的,並且所有結點的度數均爲偶數。
  • 歐拉通路:無向圖 G =< V, E > 具有一條歐拉通路,當且僅當 G 是連通的,且僅有零個或兩個奇度數結點。

有向歐拉圖的判定定理

  • 歐拉通路:有向圖 G 具有一條歐拉通路,當且僅當 G 是連通的,且除了兩個結點以外,其餘結點的入度等 於出度,而這兩個例外的結點中,一個結點的入度比出度大 1,另一個結點的出度比入度大 1。
  • 歐拉回路:有向圖 G 具有一條歐拉回路,當且僅當 G 是連通的,且所有結點的入度等於出度。

本題思路

而本題,是無向圖的歐拉回路的判定,所以我們需要做兩件事情

  1. 判定該圖是否是連通的(使用並查集,有關並查集可以參考本文
  2. 判定該圖是否所有的點的度數度數都是偶數(讀入時記錄)

完整代碼實現如下:

/* 
    歐拉圖:
        設圖 G 是一個沒有孤立結點的圖,如果存在一條迴路經過圖中
        的每條邊一次且僅一次,則稱這條迴路爲該圖的一條歐拉回路,
        含有歐拉回路的圖稱爲歐拉圖

    歐拉通路:
        無向圖 G =< V, E > 具有一條歐拉通路,當且僅當 G 是連通的,且僅有零個或兩個奇度數結點

    歐拉回路的判定定理:
        無向圖 G =< V, E > 具有一條歐拉回路,當且僅當 G 是連通的,並且所有結點的度數均爲偶數。

    採用並查集+度數的判定即可

 */
#include <iostream>
#include <vector>
using namespace std;

const int maxn = 1010;
vector<int> G[maxn]; // 保存圖
int father[maxn];    // 並查集的使用
int height[maxn];    // 保存每個結點的高度
int degree[maxn];    // 保存每個節點的度數

void Initial(int n)
{
    fill(degree, degree + n, 0);
    for (int i = 1; i <= n; i++)
    {
        father[i] = i;
        height[i] = 1;
    }
}

// 找父結點
int findFather(int x)
{
    while (x != father[x])
        x = father[x];
    return x;
}

// 合併
void Union(int x, int y)
{
    x = findFather(x);
    y = findFather(y);
    if (x != y)
    {
        if (height[x] < height[y])
        {
            father[x] = y;
        }
        else if (height[x] > height[y])
        {
            father[y] = x;
        }
        else
        {
            father[x] = y;
            height[y]++;
        }
    }
}

int main()
{
    freopen("data.txt", "r", stdin);
    int n, m;
    scanf("%d%d", &n, &m);
    Initial(n); // 初始化
    int u, v;
    for (int i = 0; i < m; i++)
    {
        scanf("%d%d", &u, &v);
        Union(u, v);
        degree[u]++;
        degree[v]++;
    }
    bool flag = true;
    int root = findFather(1);
    for (int i = 1; i <= n; i++)
    { // 判斷是否所有的點都在集合中
        if (findFather(i) != root)
        {
            flag = false;
            break;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (degree[i] % 2 != 0)
        {
            flag = false;
            break;
        }
    }
    if (flag)
    {
        printf("1");
    }
    else
    {
        printf("0");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章