zoukankan      html  css  js  c++  java
  • 搜索---数独类问题(学习笔记)

    基础篇

    给定一个未填好的(9*9)的数独,输出完整的数独.

    数独最最最基础的是要掌握行,列和一个九宫格的表示方法,其它的其实就和八皇后问题的思想差不多了.

    行和列很容易,重点就是一个小九宫格(如何由行,列得到处于第几个九宫格?)了,直接给出:

    ge[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1
    

    ((i-1)/3*3+(j-1)/3+1)如果不能理解为什么是这个式子,就记住吧(一般数独题都要用得到),但手动模拟几下,其实还是很好理解的.

    int a[10][10],hang[10][10],lie[10][10],ge[10][10];
    void print(){//输出完整的九宫格
        for(int i=1;i<=9;i++){
    		for(int j=1;j<=9;j++)
    	    	printf("%d ",a[i][j]);
    		printf("
    ");
        }
        exit(0);//这里不能用return 0,就用这个吧.
    }
    void dfs(int x,int y){
        if(a[x][y]){
    		if(x==9&&y==9)print();
    		if(y==9)dfs(x+1,1);
    		else dfs(x,y+1);
        }
    //如果[x,y]位置上已经有值了,则分类讨论如何走下去
    //这个else一定要加
        else for(int i=1;i<=9;i++){
    	    	if(!hang[x][i]&&!lie[y][i]&&!ge[(x-1)/3*3+(y-1)/3+1][i]){
    				a[x][y]=i;
    				hang[x][i]=1;
    				lie[y][i]=1;
    				ge[(x-1)/3*3+(y-1)/3+1][i]=1;
    //标记
    				if(x==9&&y==9)print();
    				if(y==9)dfs(x+1,1);
    				else dfs(x,y+1);
    //同上,走完一步后,分类讨论如何继续下去
    				a[x][y]=0;
    				hang[x][i]=0;
    				lie[y][i]=0;
    				ge[(x-1)/3*3+(y-1)/3+1][i]=0;
    //回溯
    	    	}
    		}	
    }
    int main(){
        for(int i=1;i<=9;i++)
    		for(int j=1;j<=9;j++){
    	    	a[i][j]=read();
    	    	if(a[i][j]){
    				hang[i][a[i][j]]=1;
    				lie[j][a[i][j]]=1;
    				ge[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1;
    	    	}
    //如果[i,j]位置上是x,则第i行,第j列
    //和[i,j]所在的小九宫格都不能放x了,这里做个标记
    		}
        dfs(1,1);//从[1,1]开始搜索
        return 0;
    }
    
    

    提高篇

    数独升级了!!!本题中,数独与靶结合,(9*9)的正方形中不同区域还有不同的权值,如图所示:

    我们现在要求如何填数独,能够使得获得的总分最大.每一个小格子的获得分数是该格子的权值与该格子上填的数的乘积.

    注意:当数独无解时,输出-1;

    我们在搜索时大可不必管什么最大得分,就每次搜索完之后比较一下大小就行了.但因为题目这样出,显然就是会有很多个成立的填数独的方法,所以我们现在最大的问题是,如何不超时!!!那么搜索就要考虑如何剪枝了.

    剪枝:不用解释也能明白吧,从可能情况最少的那开始填,能够大大减少搜索状态.

    但别看这个剪枝原理很简单,为了它,搜索前期得做很多工作.

    int num,now,ans=-1;//ans赋初值-1,就懒得特判了
    int a[10][10],dfs[101][5];
    int hang[10][10],lie[10][10],ge[10][10];
    struct sodu{
        int sum,line;
    }b[10];
    //sum和line记录的就是每一行各自没有填的格子的数量
    bool cmp(sodu x,sodu y){return x.sum<y.sum;}
    //按照没有填的格子的数量从小到大排序
    int point(int x,int y){
        if(x==1||y==1||x==9||y==9)return 6;
        if(x==2||y==2||x==8||y==8)return 7;
        if(x==3||y==3||x==7||y==7)return 8;
        if(x==4||y==4||x==6||y==6)return 9;
        return 10;
    }
    //通过一个函数解决每个格子的带权值问题
    void DFS(int x,int val){
        if(x>num){
    		ans=max(ans,val);
    		return;
        }
    //数独填完了,就比较一下大小.
        for(int i=1;i<=9;i++){
    		if(!hang[dfs[x][0]][i]&&!lie[dfs[x][1]][i]&&!ge[dfs[x][2]][i]){
    	    	hang[dfs[x][0]][i]=1;
    	    	lie[dfs[x][1]][i]=1;
    	    	ge[dfs[x][2]][i]=1;
    	    	DFS(x+1,val+dfs[x][3]*i);
    	    	hang[dfs[x][0]][i]=0;
    	    	lie[dfs[x][1]][i]=0;
    	    	ge[dfs[x][2]][i]=0;
    		}
        }
    }
    int main(){
        for(int i=1;i<=9;i++)
        	b[i].line=i;
    //结构体排序的时候行号不跑丢
        for(int i=1;i<=9;i++)
    	for(int j=1;j<=9;j++){
    	    a[i][j]=read();
    	    if(a[i][j]){
    			hang[i][a[i][j]]=1;
    			lie[j][a[i][j]]=1;
    			ge[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1;
    			now+=point(i,j)*a[i][j];
    	    }
    //如果[i,j]上直接有初值,除了记录行,列,格的情况
    //把已经获得的分数也累积进now
    	    else b[i].sum++;
    //如果[i,j]上没有初值,则第i行上没填的格子的数量加1
    	}
        sort(b+1,b+9+1,cmp);
        for(int i=1;i<=9;i++)
    	for(int j=1;j<=9;j++){
    	    if(a[b[i].line][j]==0){
    			dfs[num][0]=b[i].line;
    			dfs[num][1]=j;
    			dfs[num][2]=(b[i].line-1)/3*3+(j-1)/3+1;
    			dfs[num][3]=point(b[i].line,j);
                num++;
    	    }
    	}
    //num直接记录的是整个9*9上没填数的格子的数量
    //dfs数组第一维其实就是搜索顺序
    //即dfs[0][]就是我们第一个开始搜索填数的对象
    //根据上面的结构体排序,它位于未填格子数最少的一行
    //dfs[][0]:行;[1]:列;[2]:格;[3]:权值
        DFS(0,now);
        printf("%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    打造基于CentOS7的xfce最简工作环境
    Linux下C程序的编辑,编译和运行以及调试
    修正 XE6 TListView 上方 SearchBok 右边的清除钮显示
    TabControl 显示彩色的图示 (XE6 Firemonkey)
    TSwitch 中文简繁显示支持(XE6 Android)
    改变 TMemo 的背景颜色 (Firemonkey)
    修正 XE5 Android 键盘三个问题
    Delphi 收藏
    展示 Popup 的使用方法
    ListView 下拉更新 (支持 Android)
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10309771.html
Copyright © 2011-2022 走看看