我們來玩一筆畫遊戲吧,規則是這樣的:有一個連通的圖,可否找到一個剛好包含了全部的邊,而且沒有重複的路徑。java
輸入包含多組數據。每組數據的第一行包含兩個整數n和m (2≤n, m≤1000),其中n是頂點的個數,m是邊的條數。緊接着有m行,每行包含兩個整數from和to (1 ≤ from, to ≤ n, from != to),分別表明邊的兩端頂點。邊是雙向的,而且兩個頂點之間可能不止一條邊。算法
對應每一組輸入,若是能一筆畫則輸出「Yes」;不然輸出「No」。數組
3 3 1 2 2 3 1 3 4 7 1 2 2 1 1 3 1 4 1 4 2 3 4 3
Yes No
題目要求一個連通的有向圖是否能夠一筆畫完。這是一個可行遍性問題,即從圖中一個頂點出發不重複地遍歷完全部的邊並回到起始頂點,這種迴路是歐拉回路。在解答該問題前先對歐拉回路相關的內容進行介紹。測試
無向圖:
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 無向圖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
首先根據輸入構造圖的鄰接矩陣,經過鄰接矩陣判斷圖是否連通,不連通說明不能夠一筆畫完,若是連通,再判斷圖是否有奇度頂點,有就不能一筆畫完,沒有就說明能夠一筆畫完。code
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; } }