繁体   English   中英

如何在图中找到最长路径

[英]How to find Longest path in graph

解决图形中最长路径的解决方案存在一个问题。 当顶点彼此靠近时,我的程序工作非常缓慢。 这是我的代码,请帮助我。

测试班

public class GraphTest : MonoBehaviour {

// Use this for initialization
void Start () {

    const int w = 5;
    const int h = 4;


    int[,] data = new int[h,w]{
        {0,0,0,1,0},
        {0,0,0,1,0},
        {0,0,1,1,0},
        {0,0,0,0,0}};

    Map map = new Map(data,w,h);

    List<Node> longest = map.longestPath();
    Debug.Log("LONGEST " + longest.Count);

    //INFO this doing for printing array with steps
    int c = longest.Count-1;
    foreach(Node n in longest)
        data[n.r,n.c] = c--;

    string st = "";
    for(int i = 0; i < h; i++ )
    {
        for(int j = 0; j < w; j++)
        {
            st += data[i,j] + ",";
        }
        st += "\n";
    }

    Debug.Log(st);
}
}

节点类

public class Node
{
public int r,c;
public int data;
public Node[] neighbors;

public Node(int r, int c, int data)
{
    this.r = r;
    this.c = c;
    this.data = data;
    neighbors = new Node[8];
}
}

地图类查找路径

public class Map
{
public static int[] xDir = {1,1,1,0,-1,-1,-1,0};
public static int[] yDir = {-1,0,1,1,1,0,-1,-1};

public Node[,] map;
public int[,] mapData;

private int width,height;
private int[,] marks;
public Map(int[,] mapData,int width,int height)
{
    this.mapData = mapData;
    this.width = width;
    this.height = height;

    createNodes();
    initNeighbors();
}
//INFO create nodes
private void createNodes()
{
    map = new Node[height,width];
    for(int i = 0; i < height; i++ )
    {
        for(int j = 0; j < width; j++)
        {
            map[i,j] = new Node(i,j,mapData[i,j]);
        }
    }
}
//INFO assign neighbor nodes
private void initNeighbors()
{
    for(int i = 0; i < height; i++ )
    {
        for(int j = 0; j < width; j++)
        {
            for(int k = 0; k < 8; k++)
            {
                if(inRange(i+yDir[k],j+xDir[k]))
                {
                    map[i,j].neighbors[k] = map[i+yDir[k],j+xDir[k]];
                }
            }
        }
    }
}

private bool inRange(int r, int c)
{
    return r < height && r >= 0 && c < width && c >= 0;
}

public List<Node> longestPath()
{
    marks = new int[height,width];
    List<Node> nodes = new List<Node>();
    int c = dfs(map[0,0],nodes);

    //INFO Iterasions count
    Debug.Log("COUNT " + c);
    return nodes;
}

private int dfs(Node node, List<Node> nodes)
{
    int i = 1;
    List<Node> longest = new List<Node>();
    List<Node> list = null;
    marks[node.r,node.c] = 1;

    for(int n = 0; n < 8; n++)
    {
        //INFO if the neighbor node is not null and same type with parent node do dfs for neighbor node
        if(node.neighbors[n] != null && 
           marks[node.neighbors[n].r,node.neighbors[n].c] == 0 &&
           node.neighbors[n].data == node.data)
        { 
            list = new List<Node>();
            i += dfs(node.neighbors[n],list);
           //INFO if the found nodes count is more than previous nodes count set new nodes to best nodes
            if(list.Count > longest.Count)
                longest = list;
        }
    }

    marks[node.r,node.c] = 0;
    longest.Add(node);
    nodes.AddRange(longest);

    return i;
}
}

正如brz所提到的,这是一个NP完全问题。 您将无法找到既有效又保证最佳的解决方案。 (或者,如果您愿意,世界上每个程序员都会为您购买啤酒。)

这并不是说您对此无能为力。 专注于您特定用例的总体形状以及任何特殊性,并决定您愿意处理多少不准确之处。

您可以做的第一件事是寻找瓶颈-一对相邻的可遍历单元格,以使它们之间除了直接单元格之外没有其他路径,起始节点和目标节点位于该对的相对侧。 对于网格情况,您可以查看恰好具有两个邻居的单元格,然后检查针对这两个邻居的瓶颈。 如果您发现瓶颈,那么恭喜您-您已将问题分解为两个子问题,每个子问题都将更快地解决。

您也可以尝试随机方法,例如模拟退火 从最短路径开始,然后对其执行一些简单的局部扰动以使其更长,例如,如果路径一部分的两个节点都未使用该路径,则将直线更改为C形。 继续这样做,直到不再需要它为止,然后从路径中随机选择两个节点,将它们之间的路径替换为最短路径,重新拉长它,然后考虑将其作为新路径。

最终,您需要记住,这不是您可以解决的最理论,最普遍的形式的问题。 您可以专注于特殊情况,并且可以放宽对精确性的要求。 理论CS已经抛弃了你。 您需要转向实用工程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM