zoukankan      html  css  js  c++  java
  • 图算法(三):两种基本图算法的应用实例

    一、隐式图搜索

    作用在解答树,是一种广度优先遍历的方法。解答树,或者更一般的状态图,在某些问题中规模本身是无限的,但是通常能嗅到如“最简单”,“最短”,“最少”等字眼,这就意味着广度优先遍历

    再回忆广度优先遍历的实现,由一个队列保存新发现的节点,并且有这样的距离关系:

    新发现的节点与源节点的距离=其父节点与源节点的距离+1

    广度优先遍历的实现:

     1 #用字典保存一个图的邻接表形式
     2 g = {'A': ['B', 'C'],
     3      'B': ['A', 'D', 'E'],
     4      'C': ['A', 'F', 'G'],
     5      'D': ['B', 'H'],
     6      'E': ['B', 'H'],
     7      'F': ['C', 'G'],
     8      'G': ['C', 'F'],
     9      'H': ['D', 'E']}
    10 
    11 pi = {node: 0 for node in g.keys()} #父节点 
    12 d = {node: 0 for node in g.keys()} #广度遍历的层次
    13 visited = {node: 0 for node in g.keys()} #访问记录
    14 
    15 def BFS(start):
    16     visited[start] = 1
    17     global q
    18     q = [] #队列,保存新发现的节点
    19     q.append(start)
    20 
    21     while len(q) > 0:
    22         v = q.pop(0)
    23         for w in g[v]:
    24             if  visited[w] == 0:
    25                 visited[w] = 1
    26                 pi[w] = v
    27                 d[w] = d[v] + 1 #层次=父节点+1
    28 
    29                 q.append(w)
    30         visited[v] = 2
    31 
    32 def main():
    33     BFS('A')
    34     print pi

    例一、最优程序问题

     有一台只有一个数据栈的计算机,支持ADD, SUB, MUL, DIV, DUP五种运算。设栈顶元素分别为a, b, c,五种运算的效果如下: 

    ADD: a, b依次出栈,就算a+b,结果压栈

    SUB: a, b依次出栈,就算b-1,结果压栈 

    MUL: a, b依次出栈,就算a*b,结果压栈

    DIV: a, b依次出栈,就算a/b,结果压栈

    DUP: a出栈,再连续把两个a压栈

    遇到一下情况,发生错误:执行DIV操作时,栈顶元素为0;执行ADD, SUB, MUL, DIV操作时栈中只有一个元素

    问题:输入(a,b),设计一个最短的程序,使得执行前栈中只有元素a,执行后栈顶元素是b

    分析:

      粗略看,这里有一个解答树,是一个五叉树。但是如果没有明显的叶子节点,无法回溯。有注意到最短这个关键词,因此本题用广度优先的方法。

    问题的难点在于怎么表示这个状态图呢?状态图中的节点有这样几个属性:pre(历史操作),下一步操作,stack(当前的栈状态)。

      遍历过程由源节点出发,在每一个当前节点,计算下一步操作改变栈状态后,扩展下一层节点入队列。退出条件无疑是当前节点计算下一步操作后等于目标值。

    程序:

    import sys
    import copy
    
    name_dict = {0: "ADD", 1: "SUB", 2: "MUL", 3: "DIV", 4: "DUP"}
    
    op_dict = {0: lambda a, b: a + b,
               1: lambda a, b: a - b,
               2: lambda a, b: a * b,
               3: lambda a, b: b / a}
    
    class Node:
        def __init__(self):
            self.pre = [] #前缀操作
            self.op = -1 #下一次操作
            self.stack = [] #当前的栈情况
    
       
    def op():
        n.pre.append(name_dict[n.op])
    
        if n.op != 4: 
            a = n.stack.pop()
            b = n.stack.pop()
            c = op_dict[n.op](a, b)
            n.stack.append(c)
        else:   
            a = n.stack.pop()
            n.stack.append(a)
            n.stack.append(a)
    
    def main(): 
        global n1
        global n2
        global n
        global q
    
        for line in sys.stdin:
            line = line.rstrip()
            s = line.split(" ")
            n1 = int(s[0])
            n2 = int(s[1])
    
            n = Node()
            n.op = 4
            n.stack.append(n1)
                                                                                                       
            q = []  
            q.append(copy.deepcopy(n))
    
            while (len(q) > 0):
                n = q[0]
                q.pop(0)
                op()    
                if n.stack[-1] == n2:
                    break                                                                              
                                                                                                       
                for i in range(4):
                    if len(n.stack) < 2:
                        continue
                    if i == 3 and n.stack[-1] == 0:
                       continue 
                    n.op = i
                    q.append(copy.deepcopy(n))     
                                                                                                       
                n.op = 4
                q.append(copy.deepcopy(n))
            
            for opp in n.pre:
                print opp
    
            print
    
    if __name__ == '__main__':
        main()    

    二、拓扑排序

      拓扑排序是深度优先遍历的一个例子。拓扑排序专门作用与有向无环图(DAG),可以用来描述事务图的因果关系,具体是经过拓扑排序的节点箭头指向均是向右

    注意点:判断有向图是否成环,即判断是否有反向边.

    例一、假设有4个变量abcd,若a<b, c<b, d<c,求在这些条件下可能的从小到大的排序

    分析:这种从小到大的排序明显的方向性

    #include<stdio.h>
    
    int g[4][4];
    int f[4];
    
    int visit[4];
    
    int counter = 3;
    
    int dfs(int);
    
    int main()
    {
        int x;  
        g[0][1] = 1;
        g[2][1] = 1;
        g[3][2] = 1;
        g[1][3] = 1;
    
        for (x = 0; x < 4; x++) 
        {
            if (visit[x] == 0)
            {       
                if (dfs(x) == -1)
                {       
                    printf("this is not a DAG
    ");
                    return -1;
                }       
            }       
        } 
        for (x = 0; x < 4; x++) 
        {
            printf("%d ", f[x]);
        }
        return 0;
    }
    
    int dfs(int v)
    {
        int w;  
        visit[v] = -1;
        for (w = 0; w < 4; w++) 
        {
            if (g[v][w] == 1)
            {       
                if (visit[w] == -1)
                {       
                    //排除反向边的情况
                    //拓扑排序只有在有向无环图中才有意义
                    return -1;
                }else if (visit[w] == 0)
                {       
                    if (dfs(w) == -1)
                    {       
                        return -1;
                    }       
                }       
            }       
        }
        visit[v] = 1;
        f[counter] = v;
        counter--;
    }

    但是,深度优先遍历最重要的应用还是回溯与递归

  • 相关阅读:
    9、scala面向对象编程之继承
    8、scala面向对象编程之对象
    7、scala面向对象编程之类
    JavaScript读书笔记
    ATM流程的实现
    Python实现简单的验证码模块
    处女作——静态页面的编写
    CSS知识速递
    HTML知识速递
    员工信息表 实现增删改查操作
  • 原文地址:https://www.cnblogs.com/zjgtan/p/3428478.html
Copyright © 2011-2022 走看看