筆畫

題目描述

  我們來玩一筆畫遊戲吧,規則是這樣的:有一個連通的圖,可否找到一個剛好包含了全部的邊,而且沒有重複的路徑。java

1.1 輸入描述:

  輸入包含多組數據。每組數據的第一行包含兩個整數n和m (2≤n, m≤1000),其中n是頂點的個數,m是邊的條數。緊接着有m行,每行包含兩個整數from和to (1 ≤ from, to ≤ n, from != to),分別表明邊的兩端頂點。邊是雙向的,而且兩個頂點之間可能不止一條邊。算法

1.2 輸出描述:

  對應每一組輸入,若是能一筆畫則輸出「Yes」;不然輸出「No」。數組

1.3 輸入例子:

3 3
1 2
2 3
1 3
4 7
1 2
2 1
1 3
1 4
1 4
2 3
4 3

 

1.4 輸出例子:

Yes
No

 

2 解題思路

  題目要求一個連通的有向圖是否能夠一筆畫完。這是一個可行遍性問題,即從圖中一個頂點出發不重複地遍歷完全部的邊並回到起始頂點,這種迴路是歐拉回路。在解答該問題前先對歐拉回路相關的內容進行介紹。測試

2.1 歐拉回路

2.1.1 歐拉通路、歐拉回路、歐拉圖

  無向圖:
  1) 設G 是連通無向圖,則稱通過G 的每條邊一次而且僅一次的路徑爲歐拉通路;
  2) 若是歐拉通路是迴路(起點和終點是同一個頂點),則稱此迴路爲歐拉回路(Euler circuit);
  3) 具備歐拉回路的無向圖G 稱爲歐拉圖(Euler graph)。
有向圖:
  1) 設D是有向圖,D的基圖連通,則稱通過D的每條邊一次而且僅一次的有向路徑爲有向歐拉通路;
  2) 若是有向歐拉通路是有向迴路,則稱此有向迴路爲有向歐拉回路(directed Euler circuit);
  3) 具備有向歐拉回路的有向圖D稱爲有向歐拉圖(directed Euler graph)。
  圖1是有向圖。
這裏寫圖片描述
圖1 有向圖和無向圖ui

2.1.2 定理及推論

  歐拉通路和歐拉回路的斷定是很簡單的,請看下面的定理及推論。
  定理2.1 無向圖G存在歐拉通路的充要條件是:
  G爲連通圖,而且G僅有兩個奇度結點(度數爲奇數的頂點)或者無奇度結點。
  推論2.1
  1) 當G是僅有兩個奇度結點的連通圖時,G的歐拉通路必以此兩個結點爲端點。
  2) 當G是無奇度結點的連通圖時,G必有歐拉回路。
  3) G爲歐拉圖(存在歐拉回路)的充分必要條件是G爲無奇度結點的連通圖。
  例如圖1(a)所示的無向圖,存在兩個奇度頂點v2和v5,因此存在歐拉通路,且歐拉通路必以這兩個頂點爲起始頂點和終止頂點;該無向圖不存在歐拉回路。圖2-1(b)所示的無向圖爲歐拉圖。
  定理2.2 有向圖D存在歐拉通路的充要條件是:
  D爲有向圖,D的基圖連通,而且全部頂點的出度與入度都相等;或者除兩個頂點外,其他頂點的出度與入度都相等,而這兩個頂點中一個頂點的出度與入度之差爲1,另外一個頂點的出度與入度之差爲-1。
  推論2.2
  1) 當D除出、入度之差爲1,-1的兩個頂點以外,其他頂點的出度與入度都相等時,D的有向歐拉通路必以出、入度之差爲1的頂點做爲始點,以出、入度之差爲-1的頂點做爲終點。
  2) 當D的全部頂點的出、入度都相等時,D中存在有向歐拉回路。
  3) 有向圖D爲有向歐拉圖的充分必要條件是D的基圖爲連通圖,而且全部頂點的出、入度都相等。
  例如圖1(c)所示的有向圖,頂點v2和v4入度和出度均爲1;頂點v1的出度爲二、入度爲1,兩者差值爲1;頂點v3的出度爲一、入度爲2,兩者相差爲-1;因此該有向圖只存在有向歐拉通路,且必須以頂點v1爲始點,以頂點v3爲終點。圖1(d)所示的有向圖不存在有向歐拉通路。spa

2.2 解題步驟

  首先根據輸入構造圖的鄰接矩陣,經過鄰接矩陣判斷圖是否連通,不連通說明不能夠一筆畫完,若是連通,再判斷圖是否有奇度頂點,有就不能一筆畫完,沒有就說明能夠一筆畫完。code

3 算法實現

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * Declaration: All Rights Reserved !!!
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data3.txt"));
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int m = scanner.nextInt();

            // 記錄邊
            int[] edge = new int[m * 2];

            for (int i = 0; i < edge.length; i++) {
                edge[i] = scanner.nextInt();
            }

            if (draw(n, edge)) {
                System.out.println("Yes");
            } else {
                System.out.println("No");
            }

        }

        scanner.close();
    }

    /**
     * 圖是否能夠筆畫完(判斷無向圖是否存在歐拉通路)
     *
     * @param n    頂點點個數,頂點的編號從1到n
     * @param edge 邊的鏈接數組,兩個一塊兒表示一條邊
     * @return true:能夠一筆畫完,false:不能夠一筆畫完
     */
    private static boolean draw(int n, int[] edge) {

        int[] vertex = new int[n + 1];

        // 統計每一個頂點的度數
        for (int i : edge) {
            vertex[i]++;
        }


        ///////////////////////////////////////////////////////////////////////////////////////////
        // 無向圖G存在歐拉通路的充要條件是:G爲連通圖,而且G僅有兩個奇度結點(度數爲奇數的頂點)或者無奇度結點。
        ///////////////////////////////////////////////////////////////////////////////////////////

        // 統計奇度頂點個數
        int count = 0;
        for (int i = 1; i < vertex.length; i++) {
            if (vertex[i] % 2 != 0) {
                count++;
            }
        }

        // 奇度頂點不爲0且不爲2說明不存在歐拉通路
        if (count != 0 && count != 2) {
            return false;
        }


        // 構造邊的鄰接矩陣
        int[][] graph = new int[n + 1][n + 1];

        for (int i = 0; i < edge.length; i += 2) {
            int v = edge[i];
            int w = edge[i + 1];
            graph[v][w]++;
            graph[w][v]++;
        }

        // 清空頂號入度標記,將它做爲訪問標記使用,0表示沒有訪問過,1表示訪問過
        for (int i = 0; i < vertex.length; i++) {
            vertex[0] = 0;
        }

        List<Integer> list = new ArrayList<>(n);

        // 有向圖連通,那麼從任意一個頂點均可以訪問到其它的頂點
        // 從第一個頂點開始訪問,進行廣度優先遍歷
        vertex[1] = 1;
        list.add(1);
        while (!list.isEmpty()) {
            int v = list.remove(0);
            for (int i = 1; i <= n; i++) {
                // 邊(v, i),t爲0說明v不能直接到i
                int t = graph[v][i];
                // 若是(v, i)可達,且頂點i沒有被訪問過,就標記已經訪問過,添加到訪問隊列中
                if (t != 0 && vertex[i] == 0) {
                    vertex[i] = 1;
                    list.add(i);
                }
            }
        }


        for (int i = 1; i < vertex.length; i++) {
            // 還有頂點沒有訪問到,說明圖不連通
            if (vertex[i] == 0) {
                return false;
            }
        }

        return true;
    }
}

 

4 測試結果

這裏寫圖片描述

相關文章
相關標籤/搜索