java解一笔画完

最近,微信小游戏上有一款叫做一笔画完的小游戏挺好玩,但是很多关卡要卡好几天,于是我写了一段Java程序来寻找一条一笔能画完的可行路径,有像我一样懒的小伙伴可以拿去通关,不谢~

解决思路:

        一笔画完:要求在给定的网格中,从给定的起始点开始,依次不重复地经过所有格子,并且每个格子只能通往其四周(上下左右)的格子。思考的过程中,我将每一个格子看成一个点,并且将其与四周可达的点(格子看成点)相连。

        之所以这样抽象,是为了使用图论中的相关知识来建模,这样的话网格就抽象成了一个图,那么一笔画完问题也就转化为了:由指定的起点,途中经过所有其他节点且只经过一次。这就是著名的哈密顿路径。

      然后,从起点开始,选择一个默认方向的点走出,每走过一个点就将这个点和它相连的所有边从图中删除,新的点又当成起点继续寻路;那么,如果这样走下去发现是死路怎么办呢?没错,哪里来的就回哪里去,回到上一个走过的顶点,不过这里我们称之为回溯。每个点会记录其之后走过的点,回溯到上一个点后,去记录里查找还有没有没走过的点,有那就选择一个,继续往下走,没有则再回溯,..,若回溯到起点说明不存在这样的路径,图中的点已走完说明寻找到一条路径。

代码如下:(运行失败可调整虚拟机栈内存大小)

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
/**
 * vx小游戏:一笔画完 寻路
 * @author yinzw
 *
 */
public class HmdPath {

    private int m,n;//m行n列
    private int[][] edge;//邻接矩阵
    private int e = 0;//边数
    private Stack<Integer> path = new Stack<>();//记录当前路径
    private Stack<Set<Integer>> visited = new Stack<>();//记录当前路径中每个节点已经访问过的节点
    private int[] dels;//删除的点
    private int nodes;//图的顶点数
    
    
    public HmdPath(int m, int n, int ... dels) {
        super();
        this.m = m;
        this.n = n;
        this.edge = new int[m*n][m*n];
        this.dels = dels;
        this.initEdges(dels);
    }
    //开始寻路
    private boolean findPath() {
        if(path.isEmpty()) {
            System.out.println("请使用setStart(int)方法设置起始点");
            return false;
        }else
            return findPath(path.peek());
    }
    //寻路,x已经在path中
    private boolean findPath(int x) {
        //死路判断
        if(isToDeath(x)) {
            backTrack(x);
            //System.out.println("回溯");
            return findPath(path.peek());
        }
        
        //优先选择度为1的节点,存在度为1的结点要么已经成功,要么这是一条死路
        for(int j = 0; j < m * n; j++) {
            if(j != x && !visited.peek().contains(j) && edge[x][j] == 1 && getDegree(j) == 1) {
                if(path.size() == nodes - 1) {
                    //将该节点加入路径中
                    path.push(j);
                    //删除前一个节点所有边
                    delEdge(x);
                    //更新上一个节点的访问记录
                    visited.peek().add(j);
                    //初始化j节点的访问记录
                    Set<Integer> vis = new HashSet<>();
                    visited.push(vis);
                    return true;//成功!
                }else {//这是一条死路
                    //回溯
                    backTrack(x);
                    return findPath(path.peek());//
                }
            }
            
        }
        
        //其次选择度为2的节点
        for(int j = 0; j < m * n; j++) {
            if(j != x && !visited.peek().contains(j) && edge[x][j] == 1 && getDegree(j) == 2) {
                //将该节点加入路径中
                path.push(j);
                //删除前一个节点所有边
                delEdge(x);
                //更新上一个节点的访问记录
                visited.peek().add(j);
                //初始化j节点的访问记录
                Set<Integer> vis = new HashSet<>();
                visited.push(vis);
                return findPath(j);
            }
            
        }

        //任意找一个没有访问过的节点
        for(int i = 0; i < m*n; i++) {
            if(i != x && !visited.peek().contains(i) && edge[x][i] == 1) {
                //将该节点加入路径中
                path.push(i);
                //删除x结点所有边
                delEdge(x);
                //更新x结点的访问记录
                visited.peek().add(i);
                //初始化i结点的访问记录
                Set<Integer> vis = new HashSet<>();
                visited.push(vis);
                return findPath(i);
            }
        }
        
        //没有路
        if(path.size() == nodes) {
            return true;
        }
        else if(path.size() < nodes) {
            backTrack(x);
            //System.out.println("回溯");
            return findPath(path.peek());
        }else {
            System.out.println("很奇怪,没有路了");
            return false;
        }
            
        
    }
    //回溯到上一个结点
    private boolean backTrack(int x) {
        path.pop();//x出栈
        visited.pop();//删除对应的访问记录
        if(path.size() == 0) {
            System.out.println("已回溯到根结点!");
            return false;
        }
        initEdges(dels);//重新初始化图
        //path栈中除栈顶节点,删除其他所有结点连接的边,完成图的回溯
        for(int i = 0; i < path.size() - 1; i++) {
            delEdge(path.get(i));
        }
        return true;
    }
    //获取节点的度
    private int getDegree(int x) {
        int degree = 0;

        for(int j = 0; j < m * n; j++) {
            //System.out.println("edge[" + x + "][" + j + "] =" + edge[x][j]);
            if(edge[x][j] == 1 && j != x) {
                ++degree;
            }
        }    
        
        return degree;
    }
    
    private void initEdges() {
        nodes = m*n;
        e = 0;
        for(int i = 0; i < m*n; i++) {
            for(int j = 0; j < m*n; j++) {
                if(Math.abs(i - j) == 1 && Math.max(i, j)%n != 0 || Math.abs(i - j) == n) {
                    edge[i][j] = 1;
                    //System.out.println(i + "," + j + "  ");
                    if(i > j) {
                        e++;
                    }
                }else
                    edge[i][j] = 0;
            }
            
        }
    }
    
    //设置起始点
    private void setStart(int s) {
        path.push(s);
        Set<Integer> vis = new HashSet<Integer>();
        visited.push(vis);
        //findPath(s);
    }
    //初始化邻接矩阵
    private void initEdges(int ... args) {
        dels = args;
        initEdges();
        if(args != null) {
            for(int i = 0; i < args.length; i++) {
                delEdge(args[i]);    
            }
        }
        nodes = nodes - args.length;
    }
    //删除结点x相连的所有边
    private void delEdge(int x) {
        for(int j = 0; j< m * n; j++) {
            if(edge[x][j] == 1) {
                edge[x][j] = 0;
                edge[j][x] = 0;
                e--;
            }
        }
    }
    
    //判断从该结点出发是否为死路(可扩展)
    private boolean isToDeath(int x) {
        int deg2 = 0;
        for(int j = 0; j < m * n; j++) {
            if(j != x 
                    && !visited.peek().contains(j) 
                    && edge[x][j] == 1 
                    && getDegree(j) == 2) {
                deg2++;
            }
        }
        if(deg2 > 1) {
            return true;
        }else {
            return false;
        }
        
        
    }
    //打印图的邻接矩阵
    private void printEdges() {
        for(int i = 0; i < m*n; i++) {
            for(int j = 0; j< m * n; j++) {
                System.out.print(edge[i][j] + " ");
            }
            System.out.println();
        }
        
        System.out.println();
        System.out.println("当前边数为:" + e);
        
    }
     
    private void printPath() {
        System.out.println();
        System.out.println("编号如下:");
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                int num = i * n + j;
                if(num < 10) {
                    System.out.print(" " + num+ " ");
                    
                }else
                    System.out.print(num + " ");
            }
            System.out.println();
        }
        System.out.println();
        
        System.out.println("一笔画:");
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                int num = i * n + j;
                
                for(int t = 0; t < path.size();t++) {
                    if(num == path.get(t)) {
                        if(t < 10) {
                            System.out.print(" " + t+ " ");
                            
                        }else
                            System.out.print(t + " ");
                        break;
                    }else if(t < path.size() - 1) {
                        continue;
                    }
                    System.out.print("   ");
                }
            }
            System.out.println();
                
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        
        HmdPath ybhw = new HmdPath(8,6,2,7,10,26,29);   // 8和6表示8行6列的网格,2,7...29表示被挖去的格子
        ybhw.setStart(37);//起点为37
        ybhw.findPath();//寻路
        System.out.println("path:length=" + ybhw.path.size());
        ybhw.path.stream().forEach((x) -> System.out.print(x + "->") );
        ybhw.printPath();//打印寻路结果
        
        

    }

}

以上代码写的比较匆忙,未做任何优化,若有疑问或交流可发邮件至个人邮箱2583661140@qq.com

 

 

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值