• 布线问题&魔法花园_最短路径


    布线问题

    问题描述:印刷电路板将布线区域划分成n×m个方格阵列,精确的电路布线问题要求确定连接方格a到方格b的最短布线方案;布线时,电路只能沿着直线或直角(方格)布线;已经布线的方格被锁定,即不允许其它线路穿过。

    问题分析:从起始位置a开始将它作为第一个扩展结点,与该结点相邻并且可到达的方格成为可行结点被加入到活结点队列中,并且将这些方格标记为1,即从起始方格a到这些方格的距离为1。接着从活结点队列中取出队首结点作为下一个扩展结点,并将当与此时扩展结点相邻且未标记过的方格记为2,并存入活结点队列中。这个过程一直持续到算法搜索到目标方格b或活结点队列为空。

    package magicGarden;
    
    
    import java.util.LinkedList;
    import java.util.Scanner;
     
    /**
     * 布线问题
     * @author Lican
     *
     */
    public class WireRouter {
        public int[][] grid;//方格阵列;=0表示该方格允许布线;=1表示被封锁,不允许布线
        public int size;//方格阵列大小
        public int pathLen;//最短线路长度
        public LinkedList<Position> q;//扩展结点队列,用list存储
        public Position start;//起始位置
        public Position finish;//终点
        public Position[] path;//最短路径
        public WireRouter(int[][] grid,int size,Position start,Position finish){
            this.grid=grid;
            this.size=size;
            this.start=start;
            this.finish=finish;
        }
        /**
         * 方格所在位置
         * @author Lican
         *
         */
        public static class Position{
            public int row;//
            public int col;//
            
            public Position(int r ,int c){
                row=r;
                col=c;
            }
        }
        /**
         *计算从起始位置start到目标位置finish的最短布线路径
         * @return 找到最短布线路径则返回true,否则返回false
         */
        public boolean findPath(){
            if(start.row==finish.row&&start.col==finish.col){//start==finish,最短路径为0
                pathLen=0;
                return true;
            }
            
            //初始化相对位移
            Position[] offset=new Position[4];
            offset[0]=new Position(0,1);//
            offset[1]=new Position(1,0);//
            offset[2]=new Position(0,-1);//
            offset[3]=new Position(-1,0);////设置方格阵列“围墙”,方便处理方格边界的情况
            for(int i=0;i<=size+1;i++){
                grid[0][i]=grid[size+1][i]=1;//顶部和底部
                grid[i][0]=grid[i][size+1]=1;//左边和右边
            }
            
            Position here=new Position(start.row,start.col);
            grid[start.row][start.col]=2;//数字0,1表示方格的开放或封锁所以,表示距离时,让所有距离都加2;起始位置的距离为0+2=2
            int numOfNbrs=4;//相邻方格数
            
            //以下为标记可达的方格位置
            q=new LinkedList<Position>();
            Position nbr=new Position(0,0);
            do{
                //标记可达的相邻方格(每个方格有四个相邻方格)
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
                    if(grid[nbr.row][nbr.col]==0){//该方格未被标记,且该方格允许布线
                        grid[nbr.row][nbr.col]=grid[here.row][here.col]+1;
                        if(nbr.row==finish.row&&nbr.col==finish.col)
                            break;
                        q.add(new Position(nbr.row,nbr.col));
                    }
                }
                
                //检测是否到达目标位置finish
                if(nbr.row==finish.row&&nbr.col==finish.col)
                    break;
                if(q.isEmpty())
                    return false;
                
                here=q.poll();
            }while(true);
            
            //构造最短布线路径
            pathLen=grid[finish.row][finish.col]-2;
            path=new Position[pathLen];
            
            //从目标位置finish开始向起始位置回溯
            here=finish;
            for(int j=pathLen-1;j>=0;j--){
                path[j]=here;
                //找前驱位置
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
                    if(grid[nbr.row][nbr.col]==j+2)
                        break;
                }
                here=new Position(nbr.row,nbr.col);
            }
            System.out.println("最短路线为:");
            for(int j=0;j<pathLen-1;j++){
                System.out.println("点"+(j+1)+"位置:  行-"+path[j].row+" 列-"+path[j].col);
            }
            System.out.println("布线长度为:"+pathLen);
            return true;
        }
        
        
        public static void main(String[] args) {
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入方格阵列大小:");
            String s1=sc.nextLine();
            Integer size=Integer.parseInt(s1);
            
            System.out.println("请输入起始点的行和列,用空格隔开:");
            String s2=sc.nextLine();
            String[] s3=s2.split(" ");
            int startRow=Integer.parseInt(s3[0]);
            int startCol=Integer.parseInt(s3[1]);
            Position start=new Position(startRow,startCol);
            
            System.out.println("请输入结束点的行和列,用空格隔开:");
            String s4=sc.nextLine();
            String[] s5=s4.split(" ");
            int finishRow=Integer.parseInt(s5[0]);
            int finishCol=Integer.parseInt(s5[1]);
            Position finish=new Position(finishRow,finishCol);
            
            int[][] grid=new int[size+2][size+2];
            System.out.println("请输入方格阵列:");
            for(int i=1;i<=size;i++){
                String str=sc.nextLine();
                String[] strs=str.split(" ");
                for(int j=0;j<strs.length;j++){
                    grid[i][j+1]=Integer.parseInt(strs[j]);
                }
            }
            
            WireRouter w=new WireRouter(grid,size,start,finish);
            w.findPath();        
        }
    }
    /**
    请输入方格阵列大小:
    7
    请输入起始点的行和列,用空格隔开:
    3 2 
    请输入结束点的行和列,用空格隔开:
    4 6 
    请输入方格阵列:
    0 0 1 0 0 0 0
    0 0 1 1 0 0 0
    0 0 0 0 1 0 0
    0 0 0 1 1 0 0
    1 0 0 0 1 0 0
    1 1 1 0 0 0 0
    1 1 1 0 0 0 0
    最短路线为:
    点1位置:  行-3 列-3
    点2位置:  行-4 列-3
    点3位置:  行-5 列-3
    点4位置:  行-5 列-4
    点5位置:  行-6 列-4
    点6位置:  行-6 列-5
    点7位置:  行-6 列-6
    点8位置:  行-5 列-6
    布线长度为:9
    **/

    魔法花园问题

    问题描述

      哈利被困在了一个魔法花园里。魔法花园是一个 N*M 的矩形,在其中有着许多植物,这些植物会在时刻 T 消失,如果 T 是 K的倍数。哈利每单位时间都会选择上、下、左、右四个方向的其中一个进行移动。

    实验任务:已知哈利的位置和出口的位置,哈利希望你能告诉他最少需要多少时间能离开魔法花园,如果无法移动或无法找到出口,哈利会使用移形幻影。

    数据输入:第一行有一个正整数 T(1<=T<=10),表示样例数。每个样例数据的第一行有三个正整数 N、M 和 K(1<=N,M<=100,2<=K<=10),表示魔法花园是 N 行 M 列的。接下来的 N 行每行有 M 个字符,‘0’代表空的地板,“1”代表障碍,“2”代表哈利的起始位置,“3”代表出口的位置。

    数据输出:输出一个数,表示哈利离开魔法花园最少需要的时间,如果无法离开则输出“Mobiliarbus”。

    /**
    * 版本一
    */
    package
    magicGarden; import java.util.ArrayList; import java.util.LinkedList; import java.util.Scanner; /** * 布线问题 * @author Lican * */ public class WireRouter { public int[][] grid;//方格阵列;=0表示该方格允许布线;=1表示被封锁,不允许布线 public int size;//方格阵列大小 public int K;//控制植物消失的时间间隔 public int pathLen;//最短线路长度 public int timePassed=0;//从开始走总共经过多久时间 public LinkedList<Position> q;//扩展结点队列,用list存储 public Position start;//起始位置 public Position finish;//终点 public Position[] path;//最短路径 public ArrayList<Position> plant;//植物位置 private static Scanner sc; public WireRouter(int[][] grid,int size,int k,Position start,Position finish, ArrayList<Position> plant2){ this.grid=grid; this.size=size; this.K=k; this.start=start; this.finish=finish; this.plant=plant2; } /** * 方格所在位置 * @author Lican * */ public static class Position{ public int row;// public int col;// public int n;//树形结构的第几层 public Position(int r ,int c, int n1){ row=r; col=c; n=n1; } } public void disappear(boolean jud){ if(jud==true){ for(Position i:plant){ if(grid[i.row][i.col]!=1) continue; grid[i.row][i.col] = 0; } }else if(jud==false){ for(Position i:plant){ if(grid[i.row][i.col]!=0) continue; grid[i.row][i.col] = 1; } } } /** *计算从起始位置start到目标位置finish的最短布线路径 * @return 找到最短布线路径则返回true,否则返回false */ public boolean findPath(){ if(start.row==finish.row&&start.col==finish.col){//start==finish,最短路径为0 pathLen=0; return true; } //初始化相对位移 Position[] offset=new Position[4]; offset[0]=new Position(0,1,-1);// offset[1]=new Position(1,0,-1);// offset[2]=new Position(0,-1,-1);// offset[3]=new Position(-1,0,-1);////设置方格阵列“围墙”,方便处理方格边界的情况 for(int i=0;i<=size+1;i++){ grid[0][i]=grid[size+1][i]=1;//顶部和底部 grid[i][0]=grid[i][size+1]=1;//左边和右边 } Position here=new Position(start.row,start.col,start.n); grid[start.row][start.col]=2;//数字0,1表示方格的开放或封锁所以,表示距离时,让所有距离都加2;起始位置的距离为0+2=2 int numOfNbrs=4;//相邻方格数 //以下为标记可达的方格位置 q=new LinkedList<Position>(); Position nbr=new Position(0,0,-1); boolean isDisappear=false; do{ //标记可达的相邻方格(每个方格有四个相邻方格) if(nbr.n%K == 0) disappear(isDisappear=true); for(int i=0;i<numOfNbrs;i++){ nbr.row=here.row+offset[i].row; nbr.col=here.col+offset[i].col; nbr.n=here.n+1; if(grid[nbr.row][nbr.col]==0){//该方格未被标记,且该方格允许布线 grid[nbr.row][nbr.col]=grid[here.row][here.col]+1; if(nbr.row==finish.row&&nbr.col==finish.col) break; q.add(new Position(nbr.row,nbr.col,nbr.n)); } } if(isDisappear==true) disappear(isDisappear=false); //检测是否到达目标位置finish if(nbr.row==finish.row&&nbr.col==finish.col) break; if(q.isEmpty()) return false; here=q.poll(); }while(true); System.out.println("最后植物坐标:"); for(Position i:plant)//调试,输出植物坐标 System.out.println(i.row+" "+i.col); //构造最短布线路径 pathLen=grid[finish.row][finish.col]-2; path=new Position[pathLen]; //从目标位置finish开始向起始位置回溯 here=finish; for(int j=pathLen-1;j>=0;j--){ path[j]=here; //找前驱位置 for(int i=0;i<numOfNbrs;i++){ nbr.row=here.row+offset[i].row; nbr.col=here.col+offset[i].col; nbr.n=here.n+offset[i].n; if(grid[nbr.row][nbr.col]==j+2) break; } here=new Position(nbr.row,nbr.col,nbr.n); } System.out.println("最短路线为:"); for(int j=0;j<pathLen-1;j++){ System.out.println("点"+(j+1)+"位置: 行-"+path[j].row+" 列-"+path[j].col); } System.out.println("布线长度为:"+pathLen); System.out.println("转变后的地图:");//调试,输出转换后的地图,对比查看经过了哪些植物 for(int i=0;i<size;i++){ for(int j=0;j<size;j++) System.out.print(grid[i+1][j+1]+" "); System.out.println(); } return true; } public static void main(String[] args) { sc = new Scanner(System.in); System.out.println("请输入方格阵列大小:"); String s1=sc.nextLine(); Integer size=Integer.parseInt(s1); System.out.println("请输入K:"); String s6=sc.nextLine(); int K=Integer.parseInt(s6); System.out.println("请输入起始点的行和列,用空格隔开:"); String s2=sc.nextLine(); String[] s3=s2.split(" "); int startRow=Integer.parseInt(s3[0]); int startCol=Integer.parseInt(s3[1]); Position start=new Position(startRow,startCol,0); System.out.println("请输入结束点的行和列,用空格隔开:"); String s4=sc.nextLine(); String[] s5=s4.split(" "); int finishRow=Integer.parseInt(s5[0]); int finishCol=Integer.parseInt(s5[1]); Position finish=new Position(finishRow,finishCol,-1); int[][] grid=new int[size+2][size+2]; ArrayList<Position>plant = new ArrayList<Position>(); System.out.println("请输入方格阵列:"); for(int i=1;i<=size;i++){ String str=sc.nextLine(); String[] strs=str.split(" "); for(int j=0;j<strs.length;j++){ if(Integer.parseInt(strs[j])==1) plant.add(new Position(i,j+1,-1)); grid[i][j+1]=Integer.parseInt(strs[j]); } } System.out.println("初始植物坐标:"); for(Position i:plant)//调试,输出植物坐标 System.out.println(i.row+" "+i.col); WireRouter w=new WireRouter(grid,size,K,start,finish,plant); w.findPath(); } } /** 请输入方格阵列大小: 6 请输入K: 2 请输入起始点的行和列,用空格隔开: 1 4 请输入结束点的行和列,用空格隔开: 6 4 请输入方格阵列: 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 1 0 初始植物坐标: 2 4 3 2 4 4 5 4 6 3 6 5 最后植物坐标: 2 4 3 2 4 4 5 4 6 3 6 5 最短路线为: 点1位置: 行-1 列-5 点2位置: 行-2 列-5 点3位置: 行-3 列-5 点4位置: 行-4 列-5 点5位置: 行-5 列-5 点6位置: 行-6 列-5 布线长度为:7 转变后的地图: 5 4 3 2 3 4 6 5 4 1 4 5 7 6 5 6 5 6 8 7 6 1 6 7 0 8 7 8 7 8 0 0 8 9 8 9 **/

    下面的魔法花园问题是在上面布线问题的基础上改进的,其实质或者说所用算法仍是布线问题,利用分支限界法,逐层拓展,直至找到出口为止

    改进:

      1、在结点中加入n,表示拓展探试的层数,也可以理解为树结构的层数。父节点在拓展四个临近子节点时,子节点在父节点的n上加一即可

        n的用途:用于计时。每过一个单位时间,树形结构向外拓展一层,也就相当于最终路径中哈利向前走了一步

      2、加入plant数组列表,用于记录各植物的坐标

      3、加入disappear函数,用于判定植物是否消失。需注意的是,当植物消失时,如果哈利经过了植物所在地方,那么在消失时间过去之时,这个地方的植物就不能再现了,因为要地图需要记录所有探索路径,以便最后的回溯找到最优路径

    调试:

      在基本的改进思路理清后,进行代码改写,经再三确认,确定思路以及程序无误,但在测试时却总是无法运行出答案,出现 “发生内部错误 Unmatched braces in the pattern” 错误,并且无法实例化 “q=new LinkedList<Position>();”

      最后偶尔忽然明悟:原来为了方便处理边界问题,在声明地图数组时多申请了两行两列用于表示边界;所以在mian函数中记录植物位置的时候,就需要将其位置映射为程序中的row和col!

    /**
     * 版本2,加上可分别指定行列数,可从文件读入
     */
    package magicGarden;
     
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Scanner;
     
    /**
     * 布线问题
     * @author Lican
     *
     */
    public class MagicGarden {
        public int[][] grid;//方格阵列;=0表示该方格允许布线;=1表示被封锁,不允许布线
        public int n,m;//方格阵列大小
        public int K;//控制植物消失的时间间隔
        public int pathLen;//最短线路长度
        public LinkedList<Position> q;//扩展结点队列,用list存储
        public Position start;//起始位置
        public Position finish;//终点
        public Position[] path;//最短路径  
        public ArrayList<Position> plant;//植物位置
        private static Scanner in;
        public MagicGarden(int[][] grid,int n,int m,int k,Position start,Position finish, ArrayList<Position> plant2){
            this.grid=grid;
            this.n=n;
            this.m=m;
            this.K=k;
            this.start=start;
            this.finish=finish;
            this.plant=plant2;
        }
        /**
         * 方格所在位置
         * @author Lican
         *
         */
        public static class Position{
            public int row;//
            public int col;//
            public int n;//树形结构的第几层
            
            public Position(int r ,int c, int n1){
                row=r;
                col=c;
                n=n1;
            }
        }
        
        public void disappear(boolean jud){
            if(jud==true){
                for(Position i:plant){
                    if(grid[i.row][i.col]!=1)
                        continue;
                    grid[i.row][i.col] = 0;
                }
            }else if(jud==false){
                for(Position i:plant){
                    if(grid[i.row][i.col]!=0)
                        continue;
                    grid[i.row][i.col] = 1;
                }
            }
        }
        
        /**
         *计算从起始位置start到目标位置finish的最短布线路径
         * @return 找到最短布线路径则返回true,否则返回false
         */
        public boolean findPath(){
            if(start.row==finish.row&&start.col==finish.col){//start==finish,最短路径为0
                pathLen=0;
                return true;
            }
            
            //初始化相对位移
            Position[] offset=new Position[4];
            offset[0]=new Position(0,1,-1);//
            offset[1]=new Position(1,0,-1);//
            offset[2]=new Position(0,-1,-1);//
            offset[3]=new Position(-1,0,-1);////设置方格阵列“围墙”,方便处理方格边界的情况
            for(int i=0;i<=n+1;i++){
                grid[i][0]=grid[i][m+1]=1;//左边和右边
            }
            for(int j=0;j<=m+1;j++){
                grid[0][j]=grid[n+1][j]=1;//顶部和底部
            }
            
            Position here=new Position(start.row,start.col,start.n);
            grid[start.row][start.col]=4;//数字0,1表示方格的开放或封锁所以,表示距离时,让所有距离都加2;起始位置的距离为0+2=2
            int numOfNbrs=4;//相邻方格数
            
            //以下为标记可达的方格位置
            q=new LinkedList<Position>();
            Position nbr=new Position(0,0,-1);
            boolean isDisappear=false;
            do{
                //标记可达的相邻方格(每个方格有四个相邻方格)
                if(nbr.n%K == 0)
                    disappear(isDisappear=true);
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
                    nbr.n=here.n+1;
                    if(grid[nbr.row][nbr.col]==0 || grid[nbr.row][nbr.col]==3){//该方格未被标记,且该方格允许布线
                        grid[nbr.row][nbr.col]=grid[here.row][here.col]+1;
                        if(nbr.row==finish.row&&nbr.col==finish.col)
                            break;
                        q.add(new Position(nbr.row,nbr.col,nbr.n));
                    }
                }
                if(isDisappear==true)
                    disappear(isDisappear=false);
                //检测是否到达目标位置finish
                if(nbr.row==finish.row&&nbr.col==finish.col)
                    break;
                if(q.isEmpty())
                    return false;
                
                here=q.poll();
            }while(true);
    
            System.out.println("最后植物坐标:");
            for(Position i:plant)//调试,输出植物坐标
                System.out.println(i.row+" "+i.col);
            
            System.out.println("转变后的地图:");//调试,输出转换后的地图,对比查看经过了哪些植物
            for(int i=0;i<=n+1;i++){
                for(int j=0;j<=m+1;j++)
                    System.out.print(grid[i][j]+"	");
                System.out.println("
    
    ");
            }
            
            //构造最短布线路径
            pathLen=grid[finish.row][finish.col]-4;
            path=new Position[pathLen];
            
            //从目标位置finish开始向起始位置回溯
            here=finish;
            for(int j=pathLen-1;j>=0;j--){
                path[j]=here;
                //找前驱位置
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
    //            System.out.print("nbr.row:"+nbr.row+",");//调试
    //            System.out.println("nbr.col:"+nbr.col);//调试
                    nbr.n=here.n+offset[i].n;
                    if(grid[nbr.row][nbr.col]==j+4)
                        break;
                }
                here=new Position(nbr.row,nbr.col,nbr.n);
            }
            System.out.println("最短路线为:");
            for(int j=0;j<pathLen-1;j++){
                System.out.println("点"+(j+1)+"位置:  行-"+path[j].row+" 列-"+path[j].col);
            }
            System.out.println("布线长度为:"+pathLen);
            
            return true;
        }
        
        public static void main(String[] args) {
            BufferedReader sc;
            in = new Scanner(System.in);
            String name = in.next();
            try {
                sc = new BufferedReader(new FileReader(name));
                System.out.println("输入行列k,用空格隔开:");
                String s1=sc.readLine();
                String[] s6=s1.split(" ");
                int n=Integer.parseInt(s6[0]);
                int m=Integer.parseInt(s6[1]);
                int k=Integer.parseInt(s6[2]);
    
                
                int[][] grid=new int[n+2][m+2];
                Position start = null,finish = null;
                ArrayList<Position>plant = new ArrayList<Position>();
                System.out.println("请输入方格阵列:");
                for(int i=1;i<=n;i++){
                    String str=sc.readLine();
                    String[] strs=str.split(" ");
                    for(int j=0;j<strs.length;j++){
                        if(Integer.parseInt(strs[j])==1)
                            plant.add(new Position(i,j+1,-1));
                        else if(Integer.parseInt(strs[j])==2){
                            int startRow = i;
                            int startCol = j+1;
                            start = new Position(startRow,startCol,0);
                        }else if(Integer.parseInt(strs[j])==3){
                            int finishRow = i;
                            int finishCol = j+1;
                            finish = new Position(finishRow,finishCol,-1);
                        }
                        grid[i][j+1]=Integer.parseInt(strs[j]);
                    }
                }
            System.out.println("初始植物坐标:");
            for(Position i:plant)//调试,输出植物坐标
                System.out.println(i.row+" "+i.col);
            for(int i=1; i<=n; i++){//调试,输出读入二维数组信息
                for(int j=1; j<=m; j++)
                    System.out.print(grid[i][j]+" ");
                System.out.println();
            }
            System.out.println("Start:"+start.row+" "+start.col);
            System.out.println("Finish:"+finish.row+" "+finish.col);
            
            MagicGarden w=new MagicGarden(grid,n,m,k,start,finish,plant);
            w.findPath();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
                    
        }
    }
    /**
    map1.txt
    输入行列k,用空格隔开:
    请输入方格阵列:
    初始植物坐标:
    2 4
    3 2
    4 4
    5 4
    6 3
    6 5
    0 0 0 2 0 0 
    0 0 0 1 0 0 
    0 1 0 0 0 0 
    0 0 0 1 0 0 
    0 0 0 1 0 0 
    0 0 1 3 1 0 
    Start:1 4
    Finish:6 4
    最后植物坐标:
    2 4
    3 2
    4 4
    5 4
    6 3
    6 5
    转变后的地图:
    1    1    1    1    1    1    1    1    
    
    
    1    7    6    5    4    5    6    1    
    
    
    1    8    7    6    1    6    7    1    
    
    
    1    9    8    7    8    7    8    1    
    
    
    1    10    9    8    1    8    9    1    
    
    
    1    0    10    9    10    9    10    1    
    
    
    1    0    0    10    11    10    11    1    
    
    
    1    1    1    1    1    1    1    1    
    
    
    最短路线为:
    点1位置:  行-1 列-5
    点2位置:  行-2 列-5
    点3位置:  行-3 列-5
    点4位置:  行-4 列-5
    点5位置:  行-5 列-5
    点6位置:  行-6 列-5
    布线长度为:7
    */
    /**
     * 版本3,加上可出不去的情况,即哈利每一步都要有移动,如若第一次无法移动,则可移形幻影随机换位,再次无法移动时就报错无法出去
     */
    package magicGarden;
     
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Scanner;
     
    /**
     * 布线问题
     * @author Lican
     *
     */
    public class MagicGarden1 {
        public int[][] grid;//方格阵列;=0表示该方格允许布线;=1表示被封锁,不允许布线
        public int n,m;//方格阵列大小
        public int K;//控制植物消失的时间间隔
        public int pathLen;//最短线路长度
        public int privilegeT=0;//使用特权次数
        public LinkedList<Position> q;//扩展结点队列,用list存储
        public Position start;//起始位置
        public Position finish;//终点
        public Position[] path;//最短路径  
        public ArrayList<Position> plant;//植物位置
        private static Scanner in;
        public MagicGarden1(int[][] grid,int n,int m,int k,Position start,Position finish, ArrayList<Position> plant2){
            this.grid=grid;
            this.n=n;
            this.m=m;
            this.K=k;
            this.start=start;
            this.finish=finish;
            this.plant=plant2;
        }
        /**
         * 方格所在位置
         * @author Lican
         *
         */
        public static class Position{
            public int row;//
            public int col;//
            public int n;//树形结构的第几层
            
            public Position(int r ,int c, int n1){
                row=r;
                col=c;
                n=n1;
            }
        }
        
        public void disappear(boolean jud){
            if(jud==true){
                for(Position i:plant){
                    if(grid[i.row][i.col]!=1)
                        continue;
                    grid[i.row][i.col] = 0;
                }
            }else if(jud==false){
                for(Position i:plant){
                    if(grid[i.row][i.col]!=0)
                        continue;
                    grid[i.row][i.col] = 1;
                }
            }
        }
        
        /**
         *计算从起始位置start到目标位置finish的最短布线路径
         * @return 找到最短布线路径则返回true,否则返回false
         */
        public boolean findPath(){
            if(start.row==finish.row&&start.col==finish.col){//start==finish,最短路径为0
                pathLen=0;
                return true;
            }
            
            //初始化相对位移
            Position[] offset=new Position[4];
            offset[0]=new Position(0,1,-1);//
            offset[1]=new Position(1,0,-1);//
            offset[2]=new Position(0,-1,-1);//
            offset[3]=new Position(-1,0,-1);////设置方格阵列“围墙”,方便处理方格边界的情况
            for(int i=0;i<=n+1;i++){
                grid[i][0]=grid[i][m+1]=1;//左边和右边
            }
            for(int j=0;j<=m+1;j++){
                grid[0][j]=grid[n+1][j]=1;//顶部和底部
            }
            
            Position here=new Position(start.row,start.col,start.n);
            grid[start.row][start.col]=4;//数字0,1表示方格的开放或封锁所以,表示距离时,让所有距离都加2;起始位置的距离为0+2=2
            int numOfNbrs=4;//相邻方格数
            
            //以下为标记可达的方格位置
            q=new LinkedList<Position>();
            Position nbr=new Position(0,0,-1);
            
            int tmp=0;
            do{
                tmp=0;
                if(privilegeT==2){
                    System.out.println("Mobiliarbus");
                    System.exit(-1);
                }
                for(int i=0;i<numOfNbrs;i++){//四周都有植物
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
                    nbr.n=here.n+1;
                    if(grid[nbr.row][nbr.col]==1){
                        tmp++;
                    }
                }
                System.out.println("tmp:"+tmp);//调试
                if(tmp==4){
                    do{
                        start.row=here.row=1+(int)(Math.random()*(n-1));
                        start.col=here.col=1+(int)(Math.random()*(m-1));
                    }while(grid[here.row][here.col]==1||grid[here.row][here.col]==2||grid[here.row][here.col]==3);
                    System.out.println("第"+(privilegeT+1)+"次变幻后的起始点:"+start.row+","+start.col);
                    grid[start.row][start.col]=4;
                    privilegeT++;
                }
            }while(tmp==4);
            
            boolean isDisappear=false;
            do{
                //标记可达的相邻方格(每个方格有四个相邻方格)
                if(nbr.n%K == 0)
                    disappear(isDisappear=true);
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
                    nbr.n=here.n+1;
                    if(grid[nbr.row][nbr.col]==0 || grid[nbr.row][nbr.col]==3){//该方格未被标记,且该方格允许布线
                        grid[nbr.row][nbr.col]=grid[here.row][here.col]+1;
                        if(nbr.row==finish.row&&nbr.col==finish.col)
                            break;
                        q.add(new Position(nbr.row,nbr.col,nbr.n));
                    }
                }
                if(isDisappear==true)
                    disappear(isDisappear=false);
                //检测是否到达目标位置finish
                if(nbr.row==finish.row&&nbr.col==finish.col)
                    break;
                if(q.isEmpty())
                    return false;
                
                here=q.poll();
            }while(true);
    
            System.out.println("最后植物坐标:");
            for(Position i:plant)//调试,输出植物坐标
                System.out.println(i.row+" "+i.col);
            
            System.out.println("转变后的地图:");//调试,输出转换后的地图,对比查看经过了哪些植物
            for(int i=0;i<=n+1;i++){
                for(int j=0;j<=m+1;j++)
                    System.out.print(grid[i][j]+"	");
                System.out.println("
    
    ");
            }
            
            //构造最短布线路径
            pathLen=grid[finish.row][finish.col]-4;
            path=new Position[pathLen];
            
            //从目标位置finish开始向起始位置回溯
            here=finish;
            for(int j=pathLen-1;j>=0;j--){
                path[j]=here;
                //找前驱位置
                for(int i=0;i<numOfNbrs;i++){
                    nbr.row=here.row+offset[i].row;
                    nbr.col=here.col+offset[i].col;
    //            System.out.print("nbr.row:"+nbr.row+",");//调试
    //            System.out.println("nbr.col:"+nbr.col);//调试
                    nbr.n=here.n+offset[i].n;
                    if(grid[nbr.row][nbr.col]==j+4)
                        break;
                }
                here=new Position(nbr.row,nbr.col,nbr.n);
            }
            System.out.println("最短路线为:");
            for(int j=0;j<pathLen-1;j++){
                System.out.println("点"+(j+1)+"位置:  行-"+path[j].row+" 列-"+path[j].col);
            }
            System.out.println("布线长度为:"+pathLen);
            
            return true;
        }
        
        public static void main(String[] args) {
            BufferedReader sc;
            in = new Scanner(System.in);
            String name = in.next();
            try {
                sc = new BufferedReader(new FileReader(name));
                System.out.println("输入行列k,用空格隔开:");
                String s1=sc.readLine();
                String[] s6=s1.split(" ");
                int n=Integer.parseInt(s6[0]);
                int m=Integer.parseInt(s6[1]);
                int k=Integer.parseInt(s6[2]);
    
                
                int[][] grid=new int[n+2][m+2];
                Position start = null,finish = null;
                ArrayList<Position>plant = new ArrayList<Position>();
                System.out.println("请输入方格阵列:");
                for(int i=1;i<=n;i++){
                    String str=sc.readLine();
                    String[] strs=str.split(" ");
                    for(int j=0;j<strs.length;j++){
                        if(Integer.parseInt(strs[j])==1)
                            plant.add(new Position(i,j+1,-1));
                        else if(Integer.parseInt(strs[j])==2){
                            int startRow = i;
                            int startCol = j+1;
                            start = new Position(startRow,startCol,0);
                        }else if(Integer.parseInt(strs[j])==3){
                            int finishRow = i;
                            int finishCol = j+1;
                            finish = new Position(finishRow,finishCol,-1);
                        }
                        grid[i][j+1]=Integer.parseInt(strs[j]);
                    }
                }
            System.out.println("初始植物坐标:");
            for(Position i:plant)//调试,输出植物坐标
                System.out.println(i.row+" "+i.col);
            for(int i=1; i<=n; i++){//调试,输出读入二维数组信息
                for(int j=1; j<=m; j++)
                    System.out.print(grid[i][j]+" ");
                System.out.println();
            }
            System.out.println("Start:"+start.row+" "+start.col);
            System.out.println("Finish:"+finish.row+" "+finish.col);
            
            MagicGarden1 w=new MagicGarden1(grid,n,m,k,start,finish,plant);
            w.findPath();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
                    
        }
    }
    /**
    map2.txt
    输入行列k,用空格隔开:
    请输入方格阵列:
    初始植物坐标:
    2 4
    3 2
    4 4
    5 4
    6 3
    6 5
    0 0 0 3 0 0 
    0 0 0 1 0 0 
    0 1 0 0 0 0 
    0 0 0 1 0 0 
    0 0 0 1 0 0 
    0 0 1 2 1 0 
    Start:6 4
    Finish:1 4
    tmp:4
    第1次变幻后的起始点:1,2
    tmp:1
    最后植物坐标:
    2 4
    3 2
    4 4
    5 4
    6 3
    6 5
    转变后的地图:
    1    1    1    1    1    1    1    1    
    
    
    1    5    4    5    6    0    0    1    
    
    
    1    0    5    0    1    0    0    1    
    
    
    1    0    1    0    0    0    0    1    
    
    
    1    0    0    0    1    0    0    1    
    
    
    1    0    0    0    1    0    0    1    
    
    
    1    0    0    1    4    1    0    1    
    
    
    1    1    1    1    1    1    1    1    
    
    
    最短路线为:
    点1位置:  行-1 列-3
    布线长度为:2
    */
    
    /**
    map3.txt
    输入行列k,用空格隔开:
    请输入方格阵列:
    初始植物坐标:
    1 2
    1 6
    2 1
    2 3
    2 5
    3 2
    3 4
    3 6
    4 1
    4 3
    4 5
    5 2
    5 4
    5 6
    6 1
    6 3
    6 5
    0 1 0 3 0 1 
    1 0 1 0 1 0 
    0 1 0 1 0 1 
    1 0 1 0 1 0 
    0 1 0 1 0 1 
    1 0 1 2 1 0 
    Start:6 4
    Finish:1 4
    tmp:4
    第1次变幻后的起始点:3,1
    tmp:4
    第2次变幻后的起始点:3,3
    Mobiliarbus
    */
    /**
    map.txt
    6 6 2
    0 0 0 0 0 0
    0 0 0 1 0 0
    0 1 0 0 0 0
    0 0 0 1 0 0
    0 0 0 1 0 0
    0 0 1 0 1 0
    */
    
    /**
    map1.txt
    6 6 2
    0 0 0 2 0 0
    0 0 0 1 0 0
    0 1 0 0 0 0
    0 0 0 1 0 0
    0 0 0 1 0 0
    0 0 1 3 1 0
    */
    
    /**
    map2.txt
    6 6 2
    0 0 0 3 0 0
    0 0 0 1 0 0
    0 1 0 0 0 0
    0 0 0 1 0 0
    0 0 0 1 0 0
    0 0 1 2 1 0
    */
    
    /**
    map3.txt
    6 6 2
    0 1 0 3 0 1
    1 0 1 0 1 0
    0 1 0 1 0 1
    1 0 1 0 1 0
    0 1 0 1 0 1
    1 0 1 2 1 0
    */
    

      

  • 相关阅读:
    执行预定义命令
    利用jemalloc优化mysql
    ssh增加密匙登录
    vsftpd增加ssl安全验证
    利用脚本获取mysql的tps,qps等状态信息
    innodb_buffer_pool_size 大小建议
    linux多核cpu下的负载查看
    DDoS deflate+iptables防御轻量级ddos攻击
    CentOS通过日志反查入侵
    shell读取文件每行,并执行命令
  • 原文地址:https://www.cnblogs.com/LieYanAnYing/p/12084684.html
Copyright © 2020-2023  润新知