• 搜索---数独类问题(学习笔记)


    基础篇

    给定一个未填好的(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;
    }
    
    
  • 相关阅读:
    大数据之路Week08_day02 (Flume架构介绍和安装)
    Hive调优
    hive的shell用法(脑子糊涂了,对着脚本第一行是 #!/bin/sh 疯狂执行hive -f 结果报错)
    Week08_day01 (Hive 自定义函数 UDF 一个输入,一个输出(最常用))
    Week08_day01 (Hive开窗函数 row_number()的使用 (求出所有薪水前两名的部门))
    Week08_day01 (Hive实现按照指定格式输出每七天的消费平均数)
    Week08_day01 (Hive实现WordCount计数)
    SQL中 count(*)和count(1)的对比,区别
    大数据之路week07--day07 (修改mysql默认编码)
    10进制转换成16进制的函数(自写函数模板)
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10309771.html
Copyright © 2020-2023  润新知