NOIP 2009 靶形数独

可以用跳舞链,但由于我不会,就只有秀一秀深搜技术了。

数据结构:

map[i][j]存的是填的数字;

row[x][i]表示x行i个数是否出现过;

file[x][i]表示x列i个数是否出现过;

f[x][y]返回x,y属于哪一个九宫格区域;

area[x][i] 表示第x个九宫格区域中i是否出现过。

vis[x]记录深搜的顺序;

score数组当然是得分表了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
struct point {
	int x,y;
}vis[90];
bool row[15][15],file[15][15],area[15][15];
int map[15][15],score[15][15],best=0,done=0,p=0,ok=0;
int f[10][10]=
{{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};
bool find_best(int &x,int &y){  //找到当前可填的数最少的一格 
	int i,j,cur=9,cnt,k;
	for(i=1;i<=9;i++)
		for(j=1,cnt=0;j<=9;j++)
			if(!map[i][j]){
				for(k=1;k<=9;k++)
					if(!row[i][k]&&!file[j][k]&&!area[f[i][j]][k])
						cnt++;	//计数 
					if(!cnt)return false;  //不能填了 
					if(cnt<=cur)x=i,y=j,cur=cnt; 
			}
	return true;			  
}
void search(int dep ,int cur){
	int i,j,x=0,y=0;
	if(vis[dep].x&&vis[dep].y)
		x=vis[dep].x,y=vis[dep].y;
	//如果已经知道该搜索哪一格,那就将计就计吧。 
	else if (!find_best(x,y)) return;  //到这一步已经有个格子已经不可行了 
	else   
		vis[dep].x=x,vis[dep].y=y;  //记下顺序 
	if(cur+(81-dep)*90<=best)return;   //最优化仍然不如当前的答案 
	if(dep==81){     	//已经填完 
		ok=1;
		best=max(best,cur);
		return ;
	}
	for(i=1;i<=9;i++)
	if(!row[x][i]&&!file[y][i]&&!area[f[x][y]][i]){
		map[x][y]=i;
		row[x][i]=file[y][i]=area[f[x][y]][i]=true;
		search(dep+1,cur+score[x][y]*i);
		row[x][i]=file[y][i]=area[f[x][y]][i]=false;
		map[x][y]=0;
	}
}
void set_map(){
	int i,k,j;
	score[5][5]=10;
	for(i=1;i<=9;i++)
		for(k=1;k<=9;k++){
			scanf("%d",&map[i][k]);
			score[i][k]=score[5][5]-max(abs(i-5),abs(k-5));//计算每格的得分 
			j=map[i][k]; 
			if(j){
				done++;
				row[i][j]=file[k][j]=area[f[i][k]][j]=true;
				best+=j*score[i][k];
			}
}
}
int main(){
	set_map();
	memset(vis,0,sizeof(vis));
	search(done,best);
	if(ok)cout<<best;
	else cout<<"-1";
}


你可能感兴趣的:(NOIP 2009 靶形数独)