LeetCode847. Shortest Path Visiting All Nodes

这道题折腾了两天,记录一下。

  • 思路1:

首先求出所有结点两两之间的最短路径,利用弗洛伊德算法,三个for循环,时间复杂度为n的三次方;

再利用深度优先遍历dfs求出从某一结点出发遍历完所以节点的最短路径;

最后对比各结点出发的最短路径,得出遍历所以结点的最短路径。

代码如下:

bool visitAll(int *flag, int size)
{
    int i;
    
    for(i = 0; i < size; i++)
    {
        if(flag[i] == 0)
            return false;
    }
    return true;
}
void dfs(int path[12][12], int size, int node, int *val, int *ret, int *flag)
{
    int i;
    
    //printf("node = %d\n",node);
    if(flag[node] == 1)
        return;
    else
        flag[node] = 1;
    
    if(visitAll(flag, size) && *ret > *val)
    {
        *ret = *val;
        printf("*****ret = %d*****\n",*ret);
    }
        
    for(i = 0; i < size; i++)
    {
        if(flag[i] == 0)
        {
            //printf("i = %d val = %d\n",i,*val);
            *val += path[node][i];
            dfs(path, size, i, val, ret, flag);
            *val -= path[node][i];
        }
    }
    flag[node] = 0;
}
int shortestPathLength(int** graph, int graphRowSize, int *graphColSizes) {
    int i,j,k;
    int path[12][12];
    int flag[12] = {0};     //record whether the node is visited, 0 represent not visit, 1 represent visit.
    int ret = 1000;
    int val = 0;

    for(i = 0; i < graphRowSize; i++)
    {
        for(j = 0; j < graphRowSize; j++)
            path[i][j] = 1000;
    }
    
    for(i = 0; i < graphRowSize; i++)
    {
        for(j = 0; j < graphColSizes[i]; j++)
        {
            path[i][i] = 0;
            path[i][graph[i][j]] = 1;
        }
    }
    
    for(k = 0; k < graphRowSize; k++)
        for(i = 0; i < graphRowSize; i++)
            for(j = 0; j < graphRowSize; j++)
                if(path[i][j] > path[i][k] + path[k][j])
                    path[i][j] = path[i][k] + path[k][j];
    
    for(i = graphRowSize-1; i >= 0; i--)
    {
        val = 0;
        dfs(path, graphRowSize, i, &val, &ret, flag);
        printf("i = %d ret = %d\n",i,ret);
    }
    return ret;
}

【注】这种思路,需要注意一点,从一个结点出发寻找最短路径时,并不是第一次将所有结点都访问完就可以,需要将所有可能情况都遍历一次,对比所有结果,获取最短路径。

比如状态图输入为以下数组时,

[[1,2,3],[0],[0],[0]]

所有结点两两之间最短路径数组path如下:

  结点0 结点1 结点2 结点3
结点0 0 1 1 1
结点1 1 0 2 2
结点2 1 2 0 2
结点3 1 2 2

0

由于两两结点间总是存在路径,因此从某一节点出发会出现多条遍历路径,以上述状态为例,从结点0出发,第一个遍历路径为 0 - 1 - 2 - 3 , 对应访问路径为 1 + 2 + 2 = 5 也可以是0 - 1 - 3 - 2、0 - 2 - 1 - 3等等。必须将所有情况都遍历一次,才能获取到从某一个节点出发的最短路径。因此这种方法的时间复杂度为n!结果是TLE(Time Limit Exceeded)。

  • 思路二:
/******************************************************************************
** 先求出每对定点之间的最短距离 利用弗洛伊德算法 记录在path[i][j]数组中
** 再利用一个数组记录每个可能的访问状态需要经过的最短路径 visit[1<<n][n] n为节点个数 其中数组行下标代表节点被访问的状态 1代表被访问 0代码没有被访问 
** 如6的二进制表示为0110 表示第二个节点和第三个节点被访问 其余两个节点没有被访问的状态 数组列下标表示从该节点开始访问 数组值存储这种状态的最短路径 
** 数组行下标为0时 代表所有节点都没有被访问 因此第一行数组值为0 之后的每个数组值都需要借助前面的状态和path数组
** 以6为例 0110 只有第2和第3个节点被访问 因此从第1个节点和第4个节点出发没有路径 因此相应列的数组值设为最大值10000
** 从第2个节点开始 第二个节点首先被访问 那么剩下的状态就变为0100 查看数组visit[0100]的值 并结合数组path进行判断 找到从节点j出发到达状态0100的最小路径
** 赋值给visit[0110][j]
** 最后一行为所以节点都被访问的状态 依次对比从节点j出发访问完所以节点需要的路径 将最小路径返回
*******************************************************************************/
int shortestPathLength(int** graph, int graphRowSize, int *graphColSizes) {
    int i,j,k;
    int path[12][12];
    int ret = 1000;
    int n = 1<<graphRowSize;
    int visit[1<<12][12];
    int min;
    int val;

    for(i = 0; i < graphRowSize; i++)
    {
        for(j = 0; j < graphRowSize; j++)
            path[i][j] = 1000;
    }
    
    memset(visit[0],0,graphRowSize);
    for(i = 1; i < n; i++)
    {
        for(j = 0; j < graphRowSize; j++)
            visit[i][j] = 10000;
    }
    
    for(i = 0; i < graphRowSize; i++)
    {
        for(j = 0; j < graphColSizes[i]; j++)
        {
            path[i][i] = 0;
            path[i][graph[i][j]] = 1;
        }
    }
    
    for(k = 0; k < graphRowSize; k++)
        for(i = 0; i < graphRowSize; i++)
            for(j = 0; j < graphRowSize; j++)
                if(path[i][j] > path[i][k] + path[k][j])
                    path[i][j] = path[i][k] + path[k][j];
    
    
    for(i = 1; i < n; i++)
    {
        for(j = 0; j < graphRowSize; j++)
        {
            if(i == 1<<j)
            {
                visit[i][j] = 0;
                continue;
            }
            if(i & 1<<j)
            {
                //printf("i = %d j = %d\n",i,j);
                for(k = 0; k < graphRowSize; k++)
                {
                    if(j != k)
                    {
                        min = visit[i][j];
                        val = visit[i ^ 1<<j][k] + path[j][k];
                        if(min > val)
                        visit[i][j] = val; 
                    }
                }
            }
        }
    }
        
    for(i = 0; i < graphRowSize; i ++)
    {
        val = visit[n-1][i];
        if(ret > val)
            ret = val;
    }
    
    return ret;
}


猜你喜欢

转载自blog.csdn.net/g1269420003/article/details/80606293