847. Shortest Path Visiting All Nodes(二)

输入:有N个节点的无向图,每个节点被标注为0,1,…N-1。graph[i][j]表示从节点i到节点j有一条边。
输出:每个节点都访问一次,至少需要几步。
规则:可以重复访问一个节点。
分析:这道题目看了2个星期。其实这两周也是因为加班,没有再看新的题目。在地铁上没事,就把官方的解法方式拿出来看看。发现另有感悟。体会到了书读百遍其义自见。
 我们需要返回沿着边走完N个节点需要几步。因为我们可以从任意一个节点开始走。所以我们可以先问从节点0开始,走几步能访问了所有的节点。如果我们知道这个答案,那么我们分别计算从0,1,2…N-1开始,需要几步。取最小值就是答案。
 输入示例:[[1,2,3],[0],[0],[0]]
 先计算从一个节点开始。假设从节点0开始。可能的路径:节点0->节点1->?,之后需要再经过节点0,才有出路。我认为这道题目难的地方在于,之前为了防止重复访问,一个元素被访问之后做个标记,就可以。但是这里不可以。节点0->节点1->节点0->?如果此时再访问节点1,那就进入死循环了。既要一个节点访问多次,又要防止进入死循环。那么这里去重的不是节点,而是访问过的节点路径。
 状态1:节点0->节点1,访问了节点0和1,当前节点是1。
 状态2:节点0->节点1->节点0,也是访问了节点0和1,当前节点是0。
 上面的两种状态,虽然都只有两个节点被访问,但是当前节点不同,那么能选择的路径就可以不同。所以状态1和状态2是不同的状态,不能被放弃。
 状态3:节点0->节点1->节点0->节点1,也是访问了节点0和1,当前节点是1。描述完之后就发现状态3和状态1是一样的。而状态3的步数更多。所以状态3是属于重复状态,需要放弃。
 之后的可能的状态是:
 状态4:节点0->节点1->节点0->节点2
 状态5:节点0->节点1->节点0->节点2->节点0
 状态6:节点0->节点1->节点0->节点2->节点0->节点3
 也就是说需要5步可以遍历完所有节点。
 我们可以使用DFS或者BFS遍历边。编码的重点是解决怎么记录状态。我们需要记录当前已经访问了哪些节点,当前节点是哪个。前者使用bit数组记录。1=访问了节点0;3=访问了节点0和1;2=访问了节点1… 2 N − 1 = 2^N-1= 2N1=访问了所有节点。

节点标签N-1N-23210
代表值 2 N − 1 2^{N-1} 2N1 2 N − 2 2^{N-2} 2N2 2 3 2^3 23 2 2 2^2 22 2 1 2^1 21 2 0 2^0 20

public class ShortestPathVisitingAllNodesV2 {
    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 ShortestPathVisitingAllNodesV2().shortestPathLengthDFS(graph);
        System.out.println(r);
    }
    public int shortestPathLengthBFS(int[][] graph) {
        int N = graph.length;
        Queue<State> queue = new LinkedList();
        int[][] dist = new int[1<<N][N];
        for (int[] row: dist) Arrays.fill(row, N*N);

        int startNode = 1;
        dist[1<<startNode][startNode] = 0;
        queue.offer(new State(1<<startNode,startNode));
        //BFS遍历
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i=0;i<size;i++){
                State node = queue.poll();
                int d = dist[node.cover][node.head];
                if(node.cover == (1<<N)-1) return d;

                for(int child : graph[node.head]){
                    int newCover = node.cover | 1 << child;
                    if(dist[newCover][child]>d+1){
                        dist[newCover][child] =  d +1;
                        queue.offer(new State(newCover,child));
                    }
                }
            }
        }

        throw null;
    }


    private int minDis = Integer.MAX_VALUE;
    public int shortestPathLengthDFS(int[][] graph) {
        int N = graph.length;
        int[][] dist = new int[1<<N][N];
        for (int[] row: dist) Arrays.fill(row, N*N);

        int startNode = 0;
        dist[1<<startNode][startNode] = 0;
        dfs(new State(1<<startNode,startNode),graph,N,dist);

        return minDis;
    }

    private void dfs(State state,int[][] graph,int N,int[][] dist) {
        if(state.cover == (1<<N)-1){
            minDis = Math.min(minDis,dist[state.cover][state.head]);
        }else{
            for(int child : graph[state.head]){
                int newCover = state.cover | 1 << child;
                if(dist[newCover][child]>dist[state.cover][state.head]+1){
                    dist[newCover][child] = dist[state.cover][state.head]+1;
                    dfs(new State(newCover,child),graph,N,dist);
                }
            }
        }
    }


    class State {
        int cover, head;
        State(int c, int h) {
            cover = c;
            head = h;
        }
    }
}

再进一步计算从各个节点开始。修改代码

		int startNode = 1;
        dist[1<<startNode][startNode] = 0;
        queue.offer(new State(1<<startNode,startNode));

修改为:

for(int startNode=0;startNode<N;startNode++){
            dist[1<<startNode][startNode] = 0;
            queue.offer(new State(1<<startNode,startNode));
        }

代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值