zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》 0x22深度优先搜索 数独填数问题

    题目链接:http://poj.org/problem?id=3074

    本题的重点在于利用二进制位高效地判断一个位置能填哪些数,行、列、九宫格中能填的数的用一个二进制数表示,相与便是能填的数的二进制表示,使用lowbit函数可以

    依次取出最低有效位进行搜索。基于几个常识:我们做数独的时候一定每次都是从可能性最大的点入手的,也就是行列和九宫格中都不冲突的数越少越好,所以可以每次搜索都将这样的位置找出来

    进行处理,大大降低了搜索树的分支数量。

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    char str[10][10];
    int row[9],col[9],grid[9],cnt[1<<9+5],num[1<<9+5],tot;
    int g(int x,int y){//获取九宫格的编号 
        return ((x/3)*3)+(y/3);
    }
    void flip(int i,int j,int k){//翻转二进制位,行列编号以及要用来翻转的数 
        row[i]^= (1<<k);
        col[j]^=(1<<k);
        grid[g(i,j)]^=(1<<k);
    }
    bool dfs(int now){//参数表示的是当前还剩余没填的位置数量 
        if(now==0)return true;
        
        int tmp=10,x,y; //扫描所有可以填数的位置,计算出可能性最大的位置 
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++){
                if(str[i][j]!='.')continue;
                int val=row[i]&col[j]&grid[g(i,j)];//能填的数的二进制位 
                if(cnt[val] < tmp){
                    tmp=cnt[val];
                    x=i;
                    y=j;
                }
            }
            
        int val=row[x]&col[y]&grid[g(x,y)];
        while(val){//扫描所有的有效位 
            int can = num[val&(-val)];//取出最低有效位,计算这个二进制位映射的能填的数 
            flip(x,y,can); 
            str[x][y]='1'+can;
            if(dfs(now-1))return 1;
            flip(x,y,can);//回溯,还原状态 
            str[x][y]='.';
            val-=val&(-val);
        }
        return false;
    }
    int main(){
        for(int i=0;i<(1<<9);i++)
            for(int j=i;j;j-=(j&-j))cnt[i]++;//计算一种状态中有多少个可以填的数
        
        for(int i=0;i<9;i++)
            num[1<<i]=i;//通过二进制位映射到一个可以填的数
            
        char s[100];
        while(scanf("%s",s) && s[0]!='e'){
            tot=0;
            
            for(int i=0;i<9;i++)
                for(int j=0;j<9;j++)str[i][j]=s[i*9+j];//将一维数组变成二维数组
            
            for(int i=0;i<9;i++)row[i]=col[i]=grid[i]=(1<<9)-1;//计算行列和九宫格中没有用过的状态
            for(int i=0;i<9;i++)
                for(int j=0;j<9;j++){
                    if(str[i][j]!='.')flip(i,j,str[i][j]-'1');
                    else tot++;//搜索树的深度 
                }    
    //        cout<<"tot:"<<tot<<endl;
            dfs(tot);
            for(int i=0;i<9;i++)
                for(int j=0;j<9;j++)s[i*9+j]=str[i][j];//二维数组转成一维输出
            
            puts(s); 
        }
    }
  • 相关阅读:
    webpack—从零开始配置
    多媒体标签 API(video、audio)
    node 爬虫
    node 操作数据库
    es6+
    UI 组件库 引入使用的问题
    单页应用存在 的问题
    ajax 封装(集中 认证、错误、请求loading处理)
    moment.js 时间库
    文件上传大小被限制的解决方案。
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13161675.html
Copyright © 2011-2022 走看看