pat 甲级 1126. Eulerian Path java实现

来源:互联网 发布:linux下创建用户 编辑:程序博客网 时间:2024/06/18 01:45

原题目
In graph theory, an Eulerian path is a path in a graph which visits every edge exactly once. Similarly, an Eulerian circuit is an Eulerian path which starts and ends on the same vertex. They were first discussed by Leonhard Euler while solving the famous Seven Bridges of Konigsberg problem in 1736. It has been proven that connected graphs with all vertices of even degree have an Eulerian circuit, and such graphs are called Eulerian. If there are exactly two vertices of odd degree, all Eulerian paths start at one of them and end at the other. A graph that has an Eulerian path but not an Eulerian circuit is called semi-Eulerian. (Cited from https://en.wikipedia.org/wiki/Eulerian_path)

Given an undirected graph, you are supposed to tell if it is Eulerian, semi-Eulerian, or non-Eulerian.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 2 numbers N (<= 500), and M, which are the total number of vertices, and the number of edges, respectively. Then M lines follow, each describes an edge by giving the two ends of the edge (the vertices are numbered from 1 to N).

Output Specification:

For each test case, first print in a line the degrees of the vertices in ascending order of their indices. Then in the next line print your conclusion about the graph – either “Eulerian”, “Semi-Eulerian”, or “Non-Eulerian”. Note that all the numbers in the first line must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input 1:
7 12
5 7
1 2
1 3
2 3
2 4
3 4
5 2
7 6
6 3
4 5
6 4
5 6
Sample Output 1:
2 4 4 4 4 4 2
Eulerian
Sample Input 2:
6 10
1 2
1 3
2 3
2 4
3 4
5 2
6 3
4 5
6 4
5 6
Sample Output 2:
2 4 4 4 3 3
Semi-Eulerian
Sample Input 3:
5 8
1 2
2 5
5 4
4 1
1 3
3 2
3 4
5 3
Sample Output 3:
3 3 4 3 3
Non-Eulerian

题目分析
1.欧拉回路:若图G中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。若该路径是一个圈,则称为欧拉(Euler)回路。
具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图
2.判断欧拉回路

  • 无向图欧拉图满足两个条件:(1)无向图连通(2)所有节点的度为偶数。 半欧拉图满足两个条件:(1)无向图连通(2)所有节点中,只有两个节点的度为奇数
  • 有向图欧拉图满足两个条件:(1)有向图连通(2)所有的顶点出度=入度。 半欧拉图满足两个条件:(1)有向图连通(2)有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度
    3.程序分析

  • 关于图的存储,我选择用邻接矩阵,每一行存储这个顶点的所有相连的边,元素值为顶点编号,不能为1,这点十分注意!!。每一行的第一个元素存储这个顶点的度,输入的时候进行计算,不需要循环查找。

  • 关于顶点度的输出:顶点的度在每一行的第0号元素,直接输出,并且在输出时对节点度数的奇偶性进行判断并作标记,呆会对欧拉图和半欧拉图判断时用。怎么保证输出的顶点的度的最后一个数字没有空格?第一个元素先输出,把空格放在第2个元素输出的前面,这样最后一个元素后面就没有空格,最后一个元素前面的空格在最后一个元素输出。例子:比如输出1 2 3 4,4后面没有空格。第一次只输出1;第二次输出空格2;第三次输出空格3;第四次输出空格4。这样是不是就解决了!!

  • 度输出解决了,怎么判断连通性呢?判断连通性的方法有很多,我用了比较简单的一种DFS深度优先搜索。回忆:什么是深度优先搜索?假设初始状态是图中所有顶点均未被访问,从某个顶点v出发,首次访问此顶点(初始点),然后一次从v的任何一个未被访问的邻接点出发进行深度优先遍历,直到图中所有与v路径相通的顶点都被访问到。若此时图中尚有顶点为被访问,则另选图中一个顶点作为初始点,重复上述过程,直到图中所有顶点都被访问到为止。通俗讲:就是找到一条路走到黑,走不动了回退到上一个点,找另一个未被访问的点,再一条路走到黑!DFS是通过递归可以实现。利用DFS判断连通性:随便找一个顶点进行DFS,记录DFS中走过点的个数,如果走过点的个数==顶点数,那不就是连通了!图的连通性:如果Vi到Vj之间有一条路径,那么称vi 与 vj 是连通的。如果图中任何两个顶点都连通,那么图就是连通的。注意!!!连通图不一定有一个圈,一定要注意这点!!!

4程序的细节
- 邻接矩阵的建立,对于输入的两个顶点v1和v2。因为是无向图,所以必须两次操作:graph[v1][v2]=v2,graph[v2][v1]=v1。注意存储的值是顶点编号,不是1。
- 怎么保证顶点访问与否。两种情况,当然用boolean数组啊!!
- 记录DFS中走过点的个数。对DFS遍历完,统计标记数组啊!是true就+1,不是true就不加嘛!
程序如下,,直接提交中文注释去掉

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.StreamTokenizer;public class Main {//pat必须是Main     int max = 500 + 10;     static boolean[] visit;//标记数组     static int graph[][];//邻接矩阵     void dfs(int index) {//深度优先搜索        visit[index] = true;        for (int i = 1; i < graph[index].length; i++){            if (graph[index][i]!=0){                if( !visit[graph[index][i]]){                    dfs(graph[index][i]);                }            }        }    }     boolean isConnection(boolean[] visit){//统计DFS遍历个数         int num = 0;         for (int i = 1; i < visit.length; i++) {            if(visit[i]){                num++;            }        }         if(num==visit.length-1){             return true;         }         else{             return false;         }     }    public static void main(String[] args) throws IOException {        StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));        //快速输入        int v, e;        sc.nextToken();//每个输入之前必须有        v = (int)sc.nval;        sc.nextToken();        e = (int)sc.nval;        visit = new boolean[v+1];//顶点1-v,一共v+1个        for (int i = 0; i < visit.length; i++) {            visit[i]= false;        }        graph = new int[v+1][v+1];//每一行0元素存储度,其他存储邻接点        for (int i = 1; i <= e; i++) {            int v1,v2;            sc.nextToken();            v1 = (int)sc.nval;            sc.nextToken();            v2 = (int)sc.nval;            graph[v1][v2] = v2;//v1-v2有边            graph[v1][0] ++;//v1的度            graph[v2][v1] = v1;//v2-v1有边            graph[v2][0] ++;//v2的度        }        int odd = 0;//度奇数统计        System.out.print(graph[1][0]);//保证空格        if(graph[1][0]%2!=0){            odd++;        }        for (int i = 2; i <= v; i++){            if(graph[i][0]%2!=0){                odd++;            }            System.out.print(" "+graph[i][0]);        }        System.out.println();//换行        Main m = new Main();        m.dfs(1);//DFS        if(m.isConnection(visit) ){            if(odd==0)            System.out.println("Eulerian");            else if(odd==2){                System.out.println("Semi-Eulerian");            }            else{                System.out.println("Non-Eulerian");//奇数度不是0 不是2。图连同也某用!            }        }        else{            System.out.println("Non-Eulerian");        }    }}
0 0