【POJ 3074】Sudoku【剪枝】

题意:

        一个数独问题,类似的还有POJ2676 = POJ 2918 < POJ3074 < POJ3076,按难度排序。

解法:

        大部分的题解都是用舞蹈链写的,但是实在不想学这个不太常用的算法,就一直尝试剪枝。成功剪了n小时......

两个剪枝操作:
    1.每次寻找可以填的数字最少的那个格子

    2.每次更新的时候,顺便寻找是否有一行,或者有一列只能填一个数,然后进行处理

解法:
    思路虽然不难,但是代码不太好设计,这题我从早上一直写到下午,才研究清楚如何剪枝,下面讲讲我的想法
    先用num[i][j]来记录(i,j)这个点可以填几个数字
    再用num_t[i][j][k]来记录(i,j)这个点是否可以填k这个数字,如果等于1,则可以填,如果不为1则不能填

    建立一个函数find(),用来寻找num[i][j]最小的点,从可填数字最少的点入手,实现第一个剪枝操作
    然后将find找到的(x,y)这个点传入dfs,进行搜索
    对于(x,y)这个点,1-9枚举可以填的数字,如果数字k可以填,那就先将这个数字填上,然后update(),再进行dfs递归操作

    update()有两个作用:
1.因为填上了某个数字,因此需要改变所有数字的num[i][j],求num[i][j]的时候顺便求出sum1、sum2、sum3
    · sum1[i][j]表示第i行可以填数字j
    · sum2[i][j]表示第i列可以填数字j
    · sum3[i][j]表示第i个九宫格可以填数字j

2.由于求出了sum1、sum2、sum3,就可以对sum123进行遍历,如果找到某一行的某一个点只能填一次,就将这个点的num改成1,num_t的其他值都改成0

    这样的话就可以实现成功的剪枝。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
#define per(i,a,b) for(int i = b;i >= a;i--)
using namespace std;

int row[10][10],col[10][10],grid[10][10];
int map[11][11];
//int sum1[11],sum2[11],sum3[11];  //sum1:行 sum2:列 sum3:九宫格
int num[11][11]; //num[i][j]:(i,j)处可以填几个数字
int num_t[11][11][11];	//num_t[i][j][k] == 1  i,j处可以填k
int dfs(int x,int y);
struct Po{
	int x,y;
}e[100];
int zero,tot;

void update()
{
	int sum1[11][11],sum2[11][11],sum3[11][11];
	memset(num,0,sizeof num);
	memset(sum1,0,sizeof sum1);		//sum1[i][j]表示第i行可以填数字j
	memset(sum2,0,sizeof sum2);		//sum2[i][j]表示第i列可以填数字j
	memset(sum3,0,sizeof sum3);		//sum3[i][j]表示第i个方块可以填数字j
	rep(hm,1,tot)	//总共有tot个空白处
	{
		int i = e[hm].x;
		int j = e[hm].y;
		if(!map[i][j])	//如果(i,j)这个点是空格
		{
			int h = 3*((i-1)/3)+(j-1)/3+1;	//属于哪个九宫格
			per(k,1,9)	//看k能不能填入这个空格
			{
				num_t[i][j][k] = 0;
				if(!row[i][k] && !col[j][k] && !grid[h][k])	
				{
					num[i][j]++;
					num_t[i][j][k] = 1;
					sum1[i][k]++;
					sum2[j][k]++;
					sum3[h][k]++;
				}	
			}
		}
	}
	rep(i,1,9)		//i行、i列
	{
		rep(j,1,9)	//填j的数
		{
			if(sum1[i][j] == 1)	
			{
				rep(k,1,9)	//k列
				{
					if(num_t[i][k][j] == 1)
					{
						num[i][k] = 1;		//(i,k)处只能填一个数字
						rep(hm,1,9)
							num_t[i][k][hm] = 0;
						num_t[i][k][j] = 1;	//只能填j这个数字
					}
				}
			}
		}

		rep(j,1,9)	
		{
			if(sum2[i][j] == 1)	//i列填j这个数有几种填法
			{
				rep(k,1,9)	//k行
				{
					if(num_t[k][i][j] == 1)
					{
						num[k][i] = 1;
						rep(hm,1,9)
							num_t[k][i][hm] = 0;
						num_t[k][i][j] = 1;
					}
				}
			}
		}
	}
}

Po find()
{
	Po tmp;
	tmp.x = 0,tmp.y = 0;
	Po fa;
	fa.x = -10,fa.y = -10;
	if(zero == 0) return tmp;
	int maxx = 100;
	rep(hm,1,tot)
	{
		int i = e[hm].x;
		int j = e[hm].y;
		if(!map[i][j])
		{
			if(num[i][j] == 0) return fa;
			if(num[i][j] < maxx && num[i][j] != 0)
			{
				tmp.x = i;
				tmp.y = j;
				maxx = num[i][j];
			}
		}
	}
//	cout<<maxx<<endl;
	return tmp;
}

int dfs(int x,int y)
{
	if(x == -10 && y == -10) return 0;
	if(zero == 0) return 1;
	if(x < 1 || x > 9 || y < 1 || y > 9) return 1;
	int k = 3*((x-1)/3)+(y-1)/3+1;	
	rep(i,1,9)
	{
		if(num_t[x][y][i] == 0) continue;
		if(!row[x][i] && !col[y][i] && !grid[k][i])
		{
			zero--;
			map[x][y] = i;
			row[x][i] = 1;
			col[y][i] = 1;
			grid[k][i] = 1;
			update();
			int jud;
			Po tt = find();
			jud = dfs(tt.x,tt.y); 
			if(jud){
				return jud;
			}
			else
			{
				zero++;
				map[x][y] = 0;
				row[x][i] = 0;
				col[y][i] = 0;
				grid[k][i] = 0;
				update();
			}
		}
	}
	return 0;
}

int main()
{
	char cc[100];
	while(scanf("%s",cc) && cc[0] != 'e')
	{
		rep(i,0,80){
			int x = i/9+1;
			int y;
			if(i <= 8) y = i+1;
			else y = (i+1)-(x-1)*9;
			if(cc[i] == '.') map[x][y] = 0;
			else map[x][y] = cc[i]-'0';	
		}
		memset(row,0,sizeof row);
		memset(col,0,sizeof col);
		memset(grid,0,sizeof grid);
		memset(num_t,0,sizeof num_t);
		zero = 0; tot = 0;
		rep(i,1,9){
			rep(j,1,9){
				int k = 3*((i-1)/3)+(j-1)/3+1;	
				if(map[i][j] != 0){
					row[i][map[i][j]]++;
					col[j][map[i][j]]++;
					grid[k][map[i][j]]++;
				}
				else{
					zero++;
					e[++tot].x = i;
					e[tot].y = j;
				} 
			}
		}
		update();
		Po tt = find();
		dfs(tt.x,tt.y);
		rep(i,1,9)
			rep(j,1,9)
				printf("%d",map[i][j]);
		printf("\n");
	/*	rep(i,1,9)
		{
			rep(j,1,8) printf("%d",map[i][j]);
			printf("%d\n",map[i][9]);
		}*/
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/81670136