847. Shortest Path Visiting All Nodes(三)

DP
 这道题目还可以用动态规划解决。在图论中解决最短路径问题有Dijkstra算法和bellman-ford算法。这道题目也需要用到DP。所以先学习一下这两个算法的思想和区别。
 两个算法比较
 Dijstra算法用来解决单源最短路径问题。具体内容看[文章]

算法解决问题适用范围解决思路松弛对象
Dijkstra算法单源最短路径权重是正的图贪心顶点
bellman-ford算法单源最短路径图,不可以有负权环路动态规划

ps:虽然我也不明白为什么叫松弛。只知道是不断处理,不断优化的对象。
 之所以做比较是想明白这两个算法有什么区别。以及第二个算法的思想是怎么应用在本题目。
 本题目的DP方案参考链接
 首先使用Floy算法计算任意两点之间的最短路径。接着使用递归方程dp[i][j] = Math.min(dp[i][j],dp[包含u但是不包含v 的状态][u]+dist[u][v])。

public class ShortestPathVisitingAllNodesDP {
    private int[][] dis = new int[15][15];
    private int[][] dp =new int[1<<13][13];
    public int shortestPathLength(int[][] graph) {
        int N = graph.length;
        for (int[] row: dis) Arrays.fill(row, N*N);
        for (int i=0; i<N; i++) {
            for (int j=0; j<graph[i].length; j++) {
                int u = i, v = graph[i][j];
                dis[u][v] = 1;
            }
        }
        floyd(N);
        return dp(N);
    }

    /**
     * Floy算法:任意两点之间的最短距离
     * @param n
     */
    public void floyd(int n) {
        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                    dis[i][j] = Math.min(dis[i][j], dis[i][k]+dis[k][j]);
    }

    private int dp(int n) {
        for (int[] row: dp) Arrays.fill(row, n*n);
        for (int i=0; i<n; i++)
            dp[1<<i][i] = 0;
        for (int group=1; group<(1<<n); group++)
            for (int u=0; u<n; u++)
                for (int v=0; v<n; v++) {
                    int src = 1 << u, dst = 1 << v;
                    //group包含src,但是不包含dst
                    if ((group & src)!=0 && (group & dst)==0 )
                        dp[group|dst][v] = Math.min(dp[group][u] + dis[u][v], dp[group|dst][v]);
                }
        int minDis = 0x3f3f3f3f;
        for (int i=0; i<n; i++)
            minDis = Math.min(dp[(1<<n)-1][i], minDis);
        return minDis;
    }


    public static void main(String[] args){
        int[][] graph = new int[4][];
        graph[0] = new int[]{1,2,3};
        graph[1] = new int[]{0};
        graph[2] = new int[]{0};
        graph[3] = new int[]{0};

        int r = new ShortestPathVisitingAllNodesDP().shortestPathLength(graph);
        System.out.println(r);
    }
}

代码
 
 关于FLoyd算法的介绍参考文章
 用二维数组记录任意两点之间的距离。dis[i][j]表示从i节点到j节点的距离。如果这两点之间有边,则dis的初始值是边的权重,否则是无穷大。
 如果要让两点之间(例如a,b)的路程变短,那只能引入第三点(k)。如果a->k->b的距离小于a->b的距离,那就引入k。有时候可能需要引入不止一个节点,才能找到ab之间的最短路径:a->k1->k2…->b。每个顶点可能使得另外两个节点路程变短。
 第二步,如果允许在所有节点之间跨越节点1。如果 d i s [ i ] [ 1 ] + d i s [ 1 ] [ j ] &lt; d i s [ i ] [ j ] dis[i][1]+dis[1][j]&lt;dis[i][j] dis[i][1]+dis[1][j]<dis[i][j],那么 d i s [ i ] [ j ] = d i s [ i ] [ 1 ] + d i s [ 1 ] [ j ] dis[i][j]=dis[i][1]+dis[1][j] dis[i][j]=dis[i][1]+dis[1][j]。需要用两个for循环实现替换。
 第三步,如果允许在所有节点之间跨越节点1、2。如何做呢?我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小。
 第四步,进一步计算允许跨越节点1,2,3…,一直到节点n。最后的代码就是:

public void floyd(int n) {
        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                    dis[i][j] = Math.min(dis[i][j], dis[i][k]+dis[k][j]);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值