N queens N皇后问题

What is Dynamic Programming ?
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
定义 
Dynamic Programming = recursive (递归) + Memorize (备忘录)
条件 最优子结构 (问题的最优解包含子问题的最有解) + 子问题重叠
实现方式 自底向上动态规划法 + 自顶向下动态规划法 (取决于 计算的顺序)
动态规划算法的例子
字符串算法 等 最长公共子序列 最长递增子序列 最长公共子串 编辑距离
图论有效求解法 寻找图中最短距离 所有顶点间最短路径等
矩阵连乘算法
0/1 背包问题
DESC N-Queens 问题描述:

Question:给出 n × n 的棋盘,返回 棋盘上放至 n 个 queens 的 解法
规则:queue 所在的行,列,以及对角线 不允许放置第二个queue
image.png
简化 四皇后问题:
在4×4格的国际象棋上摆放四个皇后,使其不能互相攻击,
即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
image.png

Analysis 分析
    n \* n 按照行 递归 搜索 可行 情况 ,回溯处理解
     边界条件 递归 至n行
    每个一步处理3个条件:
    S1 当前列 是否占用
    S2 当时 行是否占用
    S3 对角线位置是否被占用
    不满足条件回溯到上一个步骤
    (x-y+2 \* n) : 2n-1 条  / 对角线 值范围 \[0, 3n\]
    (x+y) 2n-1 条 \\ 对角线 值范围 \[0, 2n-1\]




class Solution{
    // 冲突条件 rows[]  hills[] dales[] 使用0 1 表示是否占用
    public boolean isNotUnderAttack( int row, int col , int n , int [] rows, int [] hills, int [] dales ) {
        int res =  rows[col] + hills[row -col + 2 * n ] + dales[row +col];
        return ( res == 0) ? true :fase ;
    }
    public int backTrack( int row , int  count , int n,, int [] rows, int [] hills, int [] dales ) {
        for ( int col = 0; col < n ; col++{
            if( isNotUnderAttack(row,col,n,rows,hills,dales){
                // place_queen 
            rows[col] = 1;
            hills[row -col + 2 * n ] = 1;
            dales[row +col ] = 1 ;

            // 边界条件  n queens  already placed 
            if( row +1  == n ) count++ ;
            else count = backTrack( row +1 , count , n, rows , hills, dales);

                // remove queen
            rows[col] = 0;
            hills[row -col + 2 * n ] = 0;
            dales[row + col] = 0;
            }
        }
        return count;
    }
    public int totalNQueens (int n){
        // 初始化 数组
        int rows[] = new int[n];
        int hills[] = new int [3 * n]; 
        int dales[] = new int [2 * n -1];
        return backTrack(0,0,n,rows,hills,dales);
    }
}
    复杂度分析:
    时间复杂度:O(n!)
    空间复杂度:O(n)

优化:
image.png

    将数组存储 改为 int 数字 表示
    数字15 1111 2^3+2^2+2^1+2^0 表示选中情况
    1<<j (1移j位 表示 2^j )
    (1<<j ) & s != 0 判断第j个元素 是否在s 里面
    (1<<j ) | r 将j位的元素 添加到 r  里面
    (1<<j ) | r  表示 行~~~~
    (1<<j ) | s1 <<1 表示 / 对角线
    (1<<j ) | s2 \>> 1 表示 \\ 对角线
    public int cnt;  
    public int n;  
    //
    public void dfs( int dep ,int r, int s1, int s2){
        if (dep == n) {
            cnt++;
            return;
        }
        for( int i=0; i<n; i++){
            int j= 1<<i;  // 当前位置
            if( ( (j&r) !=0) || ((j&s1) !=0) || ((j&s2) !=0)) { continue; };                        
            dfs(dep+1, (j|r),(j|s1)<<1, ( j|s2)>>1) ;    
            // 回溯 ?
        }
    }
    int totalNqueens(int n){
        cnt =0;
        this.n = n;
        dfs(0,0,0,0);
        return cnt;
    }
}```
    对于N皇后搜索问题,在网上也有多种解法,主流是 回溯法(另有衍生的位运算变种算法),
    但不管如何优化,回溯法都有一个致命的问题:M值不能过大(一般M=30已是极限)。
    还有复杂度更底的解法吗?当然有!
    时间复杂度O(1)
    早在1969年, 被E. J. Hoffman、J. C. Loessi 和R. C. Moore找到了潜在的数学规律,通过推导出数学公式,利用 构造法 使得该问题可在O(1) 的时间复杂度得到解。

rockyzhang
3 声望0 粉丝

JavaDeveloper at ShangHai