poj1094 拓扑排序

问题重述:
所谓不同值的递增排序的序列,是通过一个小于号的运算符,找出从最小的到最大的元素。例如,有一个有序的序列A,B,C,D。这就意味着,A<B,B<C,C<D。对于这一道问题,我们将为您提供一系列式如A<B的序列,想请您确定序列能不能确定下来
输入
输入包含多个问题的实例。每个实例的第一行都包含两个正整数n和m,第一个值n表明了排序对象的数量,其中2=<n<=26。这些排序对象将是大写字母表里的前n个字母。第二个值m表示有m个类似于A<B的关系。接下来的m行,每行包含一个类似这样的关系A<B。这些字母不会超出字母表的前n个字母的范围。当n=0,m= 0时表示输入结束。
输出
对于每个问题实例,都应输出下面三行之一:
Sorted sequence determined after xxx relations: yyy...y. Sorted sequence cannot be determined. Inconsistency found after xxx relations. 其中xxx表示排序顺序或者出现矛盾在xxx个关系后能够判定,以先到者为准,而yyy ...y是一个递增的序列。
Sample Input
4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0
Sample Output
Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

2.解题过程:
分析过程:
当读题目后,对于输出有点模糊。因为考虑所有给出的关系,如果输入的数据能够保证序列只能是三者之一,当然没问题啦,否则会有许多交叉情况。在确保输入没问题的情况下,这三种种情况的判断应该有一个先后的顺序。应该先判断矛盾,一旦出现矛盾,后面的关系就不用考虑;其次再判断是否有序,同样,一旦有序了,后面的关系也不用考虑;最后,当所有关系都考虑完后,仍然无矛盾,就判断无法确定(陷井!刚开始,这里想得不全面)。数据的输入输出很容易解决,但存储与处理就比较难了。这道题是考拓扑排序的应用,而实现拓扑排序由尝过的知识可知,有无前趋的顶点优先的拓扑排序方法,无后继的顶点优先拓扑排序方法和利用深度优先遍历对DAG拓扑排序。而这道题我决定选择比较熟悉的无前趋的顶点优先的拓扑排序方法,其抽象算法描述如下:
Poj1904(G){//优先输出无前趋的顶点
      while(G中有入度为0的顶点)do{
       从G中选择一个入度为0的顶点v且输出之;
       从G中删去v及其所有出边;
       }
      if(输出的顶点数目<|V(G)|)
        //若此条件不成立,则表示所有顶点均已输出,排序成功。
        Error("G中存在有向环,排序失败!");
     }
此外,这道题有隐含这一信息,每输入一对关系,如果判定有结果,则可以忽略后面输入数据,即使后面输入数据能改变结果,也不用管。所以应该每输入一个关系就去更新当前的图,然后进行一趟拓扑排序。一旦产生结果,再对后面的数据处理下,就可以输出结果。
编程过程:
考虑再三后,决定建立一张图,并且用邻接表去存储,该图存储了顶点和弧结点等相关信息,所有的结点的信息包括入度,出度,顶点内容,是否已访问过。初始化时,只始初化相关顶点的信息,然后每输入一条边,就去更新那张图,并且再拓扑排序一次,然后就判断,
针对三种输出,有以下三种判断:
当找不到入度为0的顶点时,但所有项点还没有访问完,则说明出现了回路,也就是有矛盾。(这里还少了一种情况没有考虑,看了别人的讨论才想到的,下面补充。)
当出现入度为0的项点不唯一时,或者是边都输入完毕但顶点还没访问完,则说明序列无法确定(分析时,没有考虑到入度为0的顶点的唯一性)。
当访问完所有顶点,则说明序列能够确定下来。
做完判断后,无论后面还有没有边再输入,都进行空操作,然后输出判断。
每一次拓扑排序,如果没法做出判断,都要把图的相关信息还原。原先拓扑排序时,只是在顶点上做出入度和访问标志的修改,故现在可以利用弧结点去还原项点的原信息。
花了很长时间,终于把代码写出来了,之后又怕超内存,又把代码优化了,比如对于顶点和图的封装,省去了很多不太必要的东西。这道题写的代码有点多,所以有很些地方很容易因为粗心造成错误,这些也调试了挺多时间。最后把测试数据通过,自己的测试也通过了,但上交时却wrong answer。于是,在这里调试了很多次,很多次都差点都放弃了。接着,上网找了个别人能够ac的代码,运行了一下,和自己的输出作比较,也改正了,但提交后还是wa。最后,在讨论区看到别人的讨论后才知道自己想漏了一种情况,即多个0入度顶点和环可以同时存在,但我判断时,却只考虑0入度顶点不唯一。
做题收获:
做这道题,可以说,心情是大起大落啊。唉,当在测试了很久找出了错误,很是兴奋,却发现还wa,有时却是改对了这边,却把另一边改错了,很是郁闷。很庆幸,因为这道题,我对netbeans的调试渐渐熟练起来(以前没用过调试)。可以说,这道题是修改出来的。刚开始,整个程序的框架都很整洁的,可以算法还没完全细想就开始去编程了,以致后来,不断地发现自己有些地方想多了,有些想少了,然后就不断地修改,使行代码又长,又难看。这点以后得注意下。
程序源码
package middle;


import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**poj1094 拓扑排序
 * 绝对是呕心沥血之作,太惨了。总是wa,不知道调试了多久,唉。。。
 * @author NC
 */
public class Poj1094 {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(new BufferedInputStream(System.in));
        int n;//n个字母
        int m;//m对关系
        int flag = 0;//输出结果类型的标志
        while (true) {
            int i = 0;
            String printString = null;
            String[] s = scanner.nextLine().split(" ");
            n = Integer.parseInt(s[0]);
            m = Integer.parseInt(s[1]);
            if (m == 0 && n == 0) {
                break;
            }
            Graph graph = new Graph(n);
            for (i = 0; i < m; i++) {
                flag = 0;
                s = scanner.nextLine().split("<");//严格输入才不会有异常
                char a = s[0].charAt(0);
                char b = s[1].charAt(0);
                graph.addCurrentNumberOfVertex(a, b);//先添加结点,之后再添加边
                graph.addArcNode(a, b);
                printString = topologicalOrder(graph, i + 1);
                //可能是零入度顶点不唯一,也可能是没有零入度的顶点,也可能能确定序列
                //注意,这些只是针对于当前已经输入的边来说,有些点还没有输入
                if (printString.startsWith("*")) {//如果有序
                    if (printString.length() - 1 == graph.getNumberOfVertex()) {
                        //针对所有顶点,有序
                        flag = 2;
                        break;
                    }
                    //虽然针对当前顶点是有续的,但输入的边不够的话,还是无法确定
                    if (i == m - 1) {
                        printString = "Sorted sequence cannot be determined.";
                        flag = 1;
                        break;
                    }
                }
                if (printString.equals("Sorted sequence cannot be determined.")) {
                    //如里无法确定序列
                    //针对所有输入的边,还是没法确定,因为有可能有很多独立点
                    if (graph.hasLoop()) {
                        printString = "Inconsistency found after " + (i + 1) + " relations.";
                        flag = 1;
                        break;
                    } else if (i == m - 1) {
                        //此时,已经输入了所有的边了
                        flag = 1;
                        break;
                    }
                }
                if (printString.startsWith("Inconsistency")) {
                    //如果有矛盾,无论是否已经输入完,就可以下判断
                    flag = 1;
                    break;
                }
                recoverGraph(graph);//每一次拓扑完后都得恢复原图
            }
            int j;//输出时用的
            if (i == m) {
                j = i;
            } else {
                j = i + 1;
                i++;
            }
            while (i < m) {//处理空操作
                scanner.nextLine();
                i++;
            }
            if (flag == 1) {
                System.out.println(printString);
            } else if (flag == 2) {
                String ss = "Sorted sequence determined after "
                        + j + " relations: "
                        + printString.substring(1, printString.length());
                System.out.println(ss + ".");
            }
        }
    }

    static String topologicalOrder(Graph graph, int m) {
        int index = graph.judge0InDegree();
        String print = "";
        while (index >= 0) {
            print += graph.deleteNode(index);//删除并输出顶点
            graph.deleteSide(index);//删除与其相关的边,这些边相连的顶点的入度减1
            index = graph.judge0InDegree();//获取0入度的顶点的下标
        }
        if (print.length() < graph.getCurrentNumberOfVertex()) {
            String s = null;
            if (index == -2)//0入度的顶点不唯一
            {
                s = "Sorted sequence cannot be determined.";
            }
            if (index == -1)//没有0入度的顶点
            {
                s = "Inconsistency found after " + m + " relations.";
            }
            return s;
        } else {
            String s = "*";//有序的标志
            return s + print;
        }
    }

    static void recoverGraph(Graph graph) {
        ArrayList<Vertex> al = graph.getVertexes();
        for (Vertex v : al) {
            if (v.isVisited()) {
                v.setVisited(false);
                v.setExisted(true);
                LinkedList<Vertex> lk = v.getArcNode();
                if (lk != null) {
                    int in = (lk.getFirst()).getInDegree();
                    for (Vertex vv : lk) {
                        graph.getVertexes().get(vv.getData() - 'A').addInDegree();
                    }
                }
            }
        }
    }
}

//顶点
class Vertex {

    private boolean existed;
    private boolean visited;
    private int inDegree;
    private char data;
    private LinkedList<Vertex> arcNode;

    public boolean isExisted() {
        return existed;
    }

    public void setExisted(boolean existed) {
        this.existed = existed;
    }

    public boolean isVisited() {
        return visited;
    }

    public void setVisited(boolean visited) {
        this.visited = visited;
    }

    public Vertex(int inDegree, char data, LinkedList arcNode,
            boolean visited, boolean existed) {
        this.inDegree = inDegree;
        this.data = data;
        this.arcNode = arcNode;
        this.visited = visited;
        this.existed = existed;
    }

    public LinkedList<Vertex> getArcNode() {
        return arcNode;
    }

    public void setArcNode(LinkedList arcNode) {
        this.arcNode = arcNode;
    }

    public char getData() {
        return data;
    }

    public void setData(char data) {
        this.data = data;
    }

    public int getInDegree() {
        return inDegree;
    }

    public void setInDegree(int inDegree) {
        this.inDegree = inDegree;
    }

    public void addInDegree() {
        this.inDegree++;
    }

    public void reduceInDegree() {
        this.inDegree--;
    }
}

//用邻接矩阵存储图
class Graph {

    //每一趟,所有的顶点A--Z之间
    private ArrayList<Vertex> vertexes;
    //总顶点数
    private int numberOfVertex;
    //每输入一条边后产生的总顶点数
    private int currentNumberOfVertex;

    public Graph(int n) {
        this.numberOfVertex = n;
        this.vertexes = new ArrayList(n);
        this.currentNumberOfVertex = 0;
        init();
    }

    public int getCurrentNumberOfVertex() {
        return currentNumberOfVertex;
    }

    public void addCurrentNumberOfVertex(char a, char b) {
        if (!this.vertexes.get(a - 'A').isExisted()) {
            this.currentNumberOfVertex++;
            this.vertexes.get(a - 'A').setExisted(true);
        }
        if (!this.vertexes.get(b - 'A').isExisted()) {
            this.currentNumberOfVertex++;
            this.vertexes.get(b - 'A').setExisted(true);
        }
    }

    public int getNumberOfVertex() {
        return numberOfVertex;
    }

    public void setNumberOfVertex(int numberOfVertex) {
        this.numberOfVertex = numberOfVertex;
    }

    public ArrayList<Vertex> getVertexes() {
        return vertexes;
    }

    public void setVertexes(ArrayList<Vertex> vertexes) {
        this.vertexes = vertexes;
    }
//初始化图的所有顶点,用ArrayList,查询快一点。

    void init() {
        for (int i = 0; i < this.numberOfVertex; i++) {
            char c = (char) ('A' + i);
            Vertex v = new Vertex(0, c, null, false, false);
            vertexes.add(v);
        }
    }

    public void addArcNode(char end, char head) {
        //重复边不能再增加入度
        //a->b 弧尾指向弧头,出度不管
        boolean isAdd = true;
        LinkedList<Vertex> lk = this.vertexes.get(end - 'A').getArcNode();
        if (lk != null) {
            for (Vertex v : lk) {
                if (v.getData() == head) {
                    isAdd = false;
                }
            }
        }

        if (isAdd) {
            this.vertexes.get(head - 'A').addInDegree();//弧头,故入度加1
            //新添一个边结点(用弧头来建),
            Vertex v = new Vertex(this.vertexes.get(head - 'A').getInDegree(),
                    head, null, false, true);//这个结点用来还原图的
            //如果边接点不存了,只存入度,应该也行的

            LinkedList firstArcNode = this.vertexes.get(end - 'A').getArcNode();
            if (firstArcNode == null) {
                firstArcNode = new LinkedList();
            }
            firstArcNode.addLast(v);
            this.vertexes.get(end - 'A').setArcNode(firstArcNode);//要设进去才行
        }
    }

    public int judge0InDegree() {
        int flag = 0;
        int index = 0;
        for (int i = 0; i < this.numberOfVertex; i++) {
            //图的弧结点,我一旦加了进去就不再改变,所以不能直接利用弧结点去判断
            if (this.vertexes.get(i).isExisted() == true
                    && this.vertexes.get(i).isVisited() == false
                    && this.vertexes.get(i).getInDegree() == 0) {
                flag++;
                index = i;
            }
        }
        switch (flag) {
            case 0:
                return -1;//表示没有入度为0的顶点
            case 1:
                return index;//返回入度为0的项点(唯一的)的下标
            default:
                return -2;//表示零度入度不唯一
        }
    }

    public int getNext0InDegree() {
        boolean hasVertex = false;//表示不存在顶点
        int i;
        //当前有n个点的话,不一定是前n个点,这里错了
        for (i = 0; i < this.numberOfVertex; i++) {
            if (this.vertexes.get(i).isExisted() == true) {
                hasVertex = true;
                if (this.vertexes.get(i).isVisited() == false
                        && this.vertexes.get(i).getInDegree() == 0) {
                    return i;//零度顶点的下标
                }
            }
        }
        //判断有环,所有点都还没有遍历完,但却找不到0度顶点
        if (hasVertex && this.numberOfVertex == i) {
            return -1;//有环
        } else {
            return -2;//没有顶点
        }
    }

    public boolean hasLoop() {
        int ind;
        int i = this.getCurrentNumberOfVertex();
        while (i > 0) {
            ind = this.getNext0InDegree();
            if (ind == -1) {
                //如果有环
                return true;
            }
            if (ind == -2) {
                //所有顶点都已经访问过了了
                return false;
            }
            this.deleteNode(ind);
            this.deleteSide(ind);
            i--;
        }
        return false;
    }

    public void deleteSide(int index) {
        LinkedList<Vertex> lk = this.vertexes.get(index).getArcNode();
        if (lk != null) {
            for (Vertex arcV : lk) {
                this.vertexes.get(arcV.getData() - 'A').reduceInDegree();
            }
        }
    }

    public char deleteNode(int index) {
        char c = 0;
        this.vertexes.get(index).setExisted(false);//删除顶点
        this.vertexes.get(index).setVisited(true);//删掉也表明是访问过的,可以作为需要还原的顶点的标志
        c = this.vertexes.get(index).getData();
        return c;
    }
}

你可能感兴趣的:(C++,c,C#)