首页 > 代码库 > Arithmetic_Thinking_Dateback

Arithmetic_Thinking_Dateback

回溯法--是一个很常见的算法求解方式,它主要是把问题所有的解都按照解空间树都列出来,然后只要不符合条件的立马“剪枝”,就是回到前面的相应的父节点,将它去除,这种方式有点像父节点生出一些子节点,若是这些子节点都不争气,没办法满足需求,就会将父节点“判刑”,因为他也是他父节点中不满足条件的一个子节点,直到根节点,然后又换一个根节点,再来一遍,这种方式就可以用递归来实现

 

上代码吧,这次解决的是一个比较常见的问题,N皇后问题,我在这里先展示4皇后的解法,更多的思想是一样的

package thinking;

/*
 * Question : 求解如何在一个N*N的棋盘上放置无冲突的N皇后,无冲突就是指每个皇后的前后左右以及斜向的45°都看不得其他皇后 ,假设N = 4
 * 
 * Thinking : 从第一行的第一个为根节点开始,往下一行放,不满足的就直接放弃,回到根节点,再找其他的子节点,继续,这样的只要不满足就回到相应的父节点继续往下找,知道找到满足的
 *              解就停止,这样的思想就是回溯思想
 * */

public class Queens_backdate {
    // 规定棋盘的大小
    private int chessboard[][]= new int[4][4];
    private int count = 0;
    
    public void initializeChessboard(){
        for(int i = 0; i<4; ++i){
            for(int j = 0; j<4; ++j){
                chessboard[i][j] = 0;
            }
        }
    }
    
    // 对皇后进行上下左右,斜向的判断 有冲突的时候就返回为true,否则就返回为false
    public boolean isConflict(int x,int y){ 
        //boolean flag = false;
        
        // 对皇后的那一列进行判断
        for(int i = 0; i<4; ++i){
            if(i!=x && chessboard[i][y] == 1){
                //System.out.println("第"+x+"行"+"第"+i+"列");
                return true;
                
            }
        }
        // 对皇后的那一行进行判断
        for(int i = 0; i<4; ++i){
            if(i!=y && chessboard[x][i] == 1){
            //System.out.println("第"+i+"行"+"第"+y+"列");
                return true;
            }
        }
        // 对皇后的左斜上45°进行判断
        for(int i = 1; i<4; ++i){
            if((x>=i && y>=i) && chessboard[x-i][y-i] == 1){
                return true;
            }
        }
        // 对皇后的右斜上45°进行判断
        for(int i=1; i<4; ++i){
            if((x-i>=0) && (y+i<4) && chessboard[x-i][y+i] == 1){
                return true;
            }
        }
        // 对皇后左斜下45°进行判断
        for(int i=1; i<4; ++i){
            if((x+i<4) && (y-i>=0) && chessboard[x+i][y-i] == 1){
                return true;
            }
        }
        // 对皇后右斜下45°进行判断
        for(int i=1; i<4; ++i){
            if((x+i<4) && (y+i<4) && chessboard[x+i][y+i] == 1){
                return true;
            }
        }
        return false;
    }
    
    // 是否可以放置皇后
    public void setQueens(int i){  // 用行来形成解树
        if(i == 4){ // 判断是否已经是走到全部可以有解的状态了,那就是找到一个解,可以return了
            for(int x = 0; x < 4; ++x){
                for(int y = 0; y<4; ++y){
                    System.out.print(chessboard[x][y]);
                }
                System.out.println();
            }
            count++;
            System.out.println();
            return;
        }
        
        for(int j=0; j<4; ++j){    
            if(isConflict(i, j) == false){ // 不冲突就是可以放置皇后
                chessboard[i][j] = 1;
                setQueens(i+1);  // 递归深度优先的遍历子解树
                chessboard[i][j] = 0; // 能走出来到这一步的都是上一步走完了,也就是说,上一步都没有可以满足条件的,那么就需要剪枝了
            }

        }
    }
    
    public static void main(String[] args) {
        Queens_backdate queens = new Queens_backdate();
        queens.initializeChessboard();
        queens.setQueens(0);
        System.out.println("count : "+queens.count);
    }
}

 

其实最重要的就只有这两个函数功能,一个是满足需求来递归安置皇后的动作,一个是安置过程中会出现的判断是否在某个位置上安置这个皇后是合适的,道理看起来是比较容易的,但是实现起来一些细节不是那么容易想到,比如这个安置皇后如何实现,思想就是从第一行第一列开始安置皇后,我这里是按行来安置,遍历每一列的所以,下一个步骤就是从第二行的第二列开始安置皇后,这个时候就要考虑到是不是有皇后会发生冲突,所以安置皇后的时候要考虑冲突的if条件语句,没有冲突的才可以安置,有冲突的就进入不了条件语句,进行列的循环,知道列循环完了,都进不了条件,安置不了的话,就只有剪枝了,回到上一行的那个节点,将它减掉,进行下一列的判断,这是一个循环了,所以就可以用一个for来搞定,只需要传入行数。具体实现如上图。

Arithmetic_Thinking_Dateback