[LeetCode][H0847]访问所有节点的最短路径(java)(BFS+位运算)(状压DP)

847. 访问所有节点的最短路径

题目描述

给出 graph 为有 N 个节点(编号为 0, 1, 2, ..., N-1)的无向连通图。 

graph.length = N,且只有节点 i 和 j 连通时,j != i 在列表 graph[i] 中恰好出现一次。

返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。

 

示例 1:

输入:[[1,2,3],[0],[0],[0]]
输出:4
解释:一个可能的路径为 [1,0,2,0,3]

示例 2:

输入:[[1],[0,2,4],[1,3,4],[2],[1,2]]
输出:4
解释:一个可能的路径为 [0,1,4,2,3]

 

提示:

  1. 1 <= graph.length <= 12
  2. 0 <= graph[i].length < graph.length

反思错误:

  1. 第一遍用的dfs,每个点进行dfs,超过2*n就停止,或者全部点都被访问截止,,,毫无疑问最后一个超时
  2. 之前没接触过状压DP,没想过用二进制位运算来代替状态,,厉害啊

解题思路:

在了解状压DP之前,首先要了解二进制位运算一些知识【这里有详细的状压DP及位运算;(我写的不会那么详细)】,

&  与运算:6(110)& 4(100) = 4(100),,,不解释了

|   或运算:6(110) |  4(100) = 6(110)

^  异或运算:6(110) ^  4(100) = 2(010)

<< 左移运算:1<<3 = 1 * 2³ = 8(1000)   将1(0001)向左移动3位 ==》  8(1000) ;简单理解就是左值乘以2的右值次幂。

>> 右移运算:相对应的就是向右移,,也就是除2

 

看这道题,拿示例一来说:

                        

图是这个样子的,,,我们首先要确定,每个点是否被访问过应该用二进制来表示,也就是

1(0001)代表0节点被访问,1、2、3没被访问。。

6(0110)代表1、2节点被访问,0、3没有………………{因为二进制数字0110从右往左数第零三位置为0,一二位置为1}

知道了每个节点的状态之后,创建dp[节点个数][所有状态都没被访问+1],,按这题来说

int[][] dp = new int[4][16]    ------------------因为我们最大状态(1111),申请的空间都是从零开始的,你不会不知道吧。我们需要申请大小为16(10000),所以是(10000),我也说不清了,,,,就类似我们数字有0、1、2、3,但是申请需要 [4] 。

然后需要两个Queue队列存储每个节点编号 id 和它对应的状态 state 

把初始每个点放入队列,让队列中每次都走一步,这样第一个出现走完所有点,也就是状态为(1111)的dp值就是最小路。

 

我觉得看代码更清晰。

Java代码:

class Solution {
    public int shortestPathLength(int[][] graph) {
        int n = graph.length;//number of points in graph
        int statelen = 1<<n;//state:已访问点的状态,四个点就是2^4(二进制10000)
        int[][] dp = new int[n][statelen];//第n点时 ,其他点访问状态state 时的 最小步数
        Queue<Integer> queueid = new LinkedList<>();
        Queue<Integer> queuestate = new LinkedList<>();
        //初始化
        int end=0;
        for(int i=0;i<n;++i){
            queueid.offer(i);
            int state = 1<<i;
            queuestate.offer(state);//1表示访问过了
            end |=state;//表示结束情况是每个点都被访问了
        }
        while(!queueid.isEmpty()){
            int id = queueid.remove();
            int state = queuestate.remove();
            if(state==end)return dp[id][state];
            for(int nextid:graph[id]){
                int nextstate = state|1<<nextid;
                if(dp[nextid][nextstate]==0) {
                    queueid.add(nextid);
                    queuestate.add(nextstate);
                    dp[nextid][nextstate] = dp[id][state]+1;
                }
            }
        }
        return -1;
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值