西西河

主题:再次请教河里的高手,一个图论问题 -- 棒棒糖

共:💬26 🌺15 新:
分页树展主题 · 全看首页 上页
/ 2
下页 末页
  • 家园 再次请教河里的高手,一个图论问题

    最近又要做工程又要做理论,

    已经晕头转向了

    这次又得请教高手们一个图论的问题

    最近的一篇文章就差这一块了:

    一个具有n个节点的有向图,

    节点按照编号大小排列

    编号小的节点可以与任意编号比它大的节点相连

    即对于节点i

    对于任意节点j

    如果 i < j

    则存在弧 i -> j

    我称之为前向通路

    而编号大的节点到编号小的节点则只存在有限个已知的连线

    我称之为后向通路

    我现在需要一个有效的算法

    能够枚举出所有这样路径

    该路径能够依次不重复遍历所有的节点

    并且如果节点i, j之间不存在后向通路

    则i, j的相对顺序在路径中不能发生改变

    在我的第一个附图里面显示了6个节点和它们之间的后向通路

    方便起见没有画出前向通路

    点看全图

    外链图片需谨慎,可能会被源头改

    第二个附图中显示了一种可能的路径,即

    1 -> 4 -> 2 -> 5 -> 3 -> 6

    点看全图

    外链图片需谨慎,可能会被源头改

    第三个附图显示了另外一种可能的路径,即

    1 -> 2 -> 3 -> 4 -> 5 -> 6

    点看全图

    外链图片需谨慎,可能会被源头改

    • 家园 一个算法描述:

      对于每个节点i构造一个集合Pi,Pi包含所有小于i且没有后向边连接的节点。这样,对于没有后向路的图,Pi分别是:

      P1=[],P2=[1],...,P[N]=[1,2,...,N-1]

      而对于你给出的图则有

      P1=[],P2=[1],P3=[1,2],P4=[1],P5=[1,2],P[6]=[1,2,3,4,5]

      枚举路径的办法同一般的深度优先遍历类似,不过每次选取节点k加入路径的时候,要求Pk为空,

      选取k后,对所有的P进行扫描,删除所有的k,

      如果选k时候有多个空集备选,则记下回溯点,如果没有任何集合为空则需要回溯。

      如果后向边不多的话,此算法应该是近似N平方的。

    • 家园 如果节点数不多,可否这样做?

      我的思路是dynamic programming:假设路径长度为N,先计算出N=2的所有情况,存储路径上的节点号。

      然后开始利用N的结果计算N+1的路径,对于有回路的,就prune.

      这里面可以优化的细节很多,首先,存储路径上节点的方式,可以帮助后来loop detection + prune。其次,存储路径上面,可以根据 i<j -> {i,j}一定存在来节省空间。最后,如果N太大,可以用divide and conquer的方法,只存储部分结果(i.e. N/2),然后用时间换空间,计算另外一半路径。

      具体的程序就不写了,主要是说说思路。

    • 家园 用c#实现的

      用一个2维数组定义所以可能的通路,比如元素paths[i,j]=0表示i节点到j节点不通,而1表示可通,那你所举的例子实现如下

      int[,] paths = new int[6,6] { { 0, 1, 1, 1, 1, 1 }, { 0, 0, 1, 1, 1, 1 }, { 0, 0, 0, 1, 1, 1 }, { 0, 1, 1, 0, 1, 1 }, { 0, 0, 1, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0 } };

      private ArrayList pathList = new ArrayList();

      private ArrayList curPath = new ArrayList();

      private void Doit()

      {

      for (int i = 0; i <= 5; i++)

      {

      GetPaths(i);

      }

      }

      private void GetPaths(int startNode)

      {

      curPath.Add(startNode);

      if (curPath.Count == 6)

      {

      pathList.Add(((int)curPath[0] + 1).ToString() + ((int)curPath[1] + 1).ToString() + ((int)curPath[2] + 1).ToString() + ((int)curPath[3] + 1).ToString() + ((int)curPath[4] + 1).ToString() + ((int)curPath[5] + 1).ToString());

      curPath.Remove(startNode);

      return;

      }

      for (int j = 0; j <= 5; j++)

      {

      if ((paths[startNode, j] == 1) && !curPath.Contains(j))

      {

      GetPaths(j);

      }

      }

      curPath.Remove(startNode);

      return;

      }

      • 家园 算法效率很重要

        虽然原问题应该是一个NPC

        但还是希望算法在一般情况下有较好的效率

        因为在实际情况下

        节点数量在100个左右

        指数爆炸是很可怕的事情

      • 家园 程序没有细看,不知道理解的对否

        程序我大致看了一下

        应该是基于穷举的思想吧

        利用递归进行搜索

        实际上形成了一棵搜索树

        这样做的效率太低了

        举个最简单的例子

        如果这六个节点都没有反向通路

        那么就只有一条路径

        也就是1->2->3->4->5->6

        但如果用这个程序搜索

        搜索复杂度恐怕会是O(6!)左右吧

      • 家园 startnodes

        startNode

        在哪里定义的?没看出来。。是ArrayList的标准函数吗?

        不好意思,没用过c#

分页树展主题 · 全看首页 上页
/ 2
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河