NOIP2015提高组 靶形数独题解

首先这题是NOIP2015提高组的最后一题,做出来的时候要上天了!感觉比较简(kun)单(nan)

下面就来讲一下我做这道题的过程

 

这道题的核心就是让你填数字。

规则有三点:

1、在3*3的小矩阵中不能出现重复的数字

2、一行也不能出现

3、一列也不能出现

70 points 做法

则我们定义三个数组,分别表示

第一个:small[i][j][k]表示第i行第j个数独k有没有出现过

第二个:hang[i][j]表示第i行第j个数有没有出现

第三个:lie[i][j]表示第i列第j个数有没有出现。

然后爆搜做法,记住每行填满了数就要换行。

100 points 做法

数组和上面一样,因为是暴力,我们要尽量减少我们爆搜的子状态来节省时间。

那么怎么节省呢?

我们不难发现,先从填数少的开始搜索最好,为什么呢?

理由:每次如果从填数多的开始枚举,那么每次的第一状态会产生更多的子状态,

反之,如果从填数少的枚举,那么每次的第一状态少,则会少去大量多余的子状态。

具体怎么实现呢?

我们可以排序,因此想到结构体,结构体的变量有两个。

1、当前的行数——xx

2、当前行0的个数——sl

我们以变量2来排序。

每次爆搜时,参数变成 now和y,x去掉

第x行改成子状态的xx节点即可。

那么每次多一行我们就多加一次now

最后分数用一个score数组存好,每次加上score[i-1][j-1],

因为我们打表是从0开始的,所以不要忘了减一,一开始犯了这个错误。

随后,记住判断如果ans=0时,证明无解,所以输出-1

代码如下:

//这道题的核心优化就是先从填的数字少开始找(也就是0多的开始找),这样会极大的减少子状态,从而从70到100
#include<cstdio>
#include<algorithm>
#include<cstring> 
using namespace std;
struct node
{
	int sl,xx;
}ss[1100]; 
bool small[5][5][10];//small[i][j][k]表示第i行第j个数独k有没有出现过 
bool hang[10][10];//hang[i][j]表示第i行第j个数有没有出现 
bool lie[10][10];//lie[i][j]表示第i列第j个数有没有出现]
int a[10][10]; 
int maxx=0;
bool cmp(node n1,node n2)
{
	return n1.sl<n2.sl;
}
int shudu[10][10]=
{
	{6,6,6,6,6,6,6,6,6},
	{6,7,7,7,7,7,7,7,6},
	{6,7,8,8,8,8,8,7,6},
	{6,7,8,9,9,9,8,7,6},
	{6,7,8,9,10,9,8,7,6},
	{6,7,8,9,9,9,8,7,6},
	{6,7,8,8,8,8,8,7,6},
	{6,7,7,7,7,7,7,7,6},
	{6,6,6,6,6,6,6,6,6},
};
void dfs(int now,int y)
{
	int x=ss[now].xx; 
	if(now==10)//填完就判断ans是多少 
	{
		int ans=0;
		for(int i=1;i<=9;i++)
		{
			for(int j=1;j<=9;j++)
			{
				ans=ans+a[i][j]*shudu[i-1][j-1];
			}
		}
		maxx=max(maxx,ans);
		return ;
	}
	if(a[x][y]!=0)
	{
		if(y==9) //一列填完填下一列 
		{
			dfs(now+1,1);
		}
		else dfs(now,y+1); 
	}
	else//填数字 
	{
		for(int i=1;i<=9;i++)
		{
			if(lie[x][i]==false&&hang[y][i]==false&&small[(x-1)/3+1][(y-1)/3+1][i]==false)
			{
				a[x][y]=i;
				lie[x][i]=true;hang[y][i]=true;small[(x-1)/3+1][(y-1)/3+1][i]=true;
				if(y==9) dfs(now+1,1);
				else dfs(now,y+1);
				a[x][y]=0;
				lie[x][i]=false;hang[y][i]=false;small[(x-1)/3+1][(y-1)/3+1][i]=false;
			}
		}
	}
}
int main()
{
	memset(hang,false,sizeof(hang));
	memset(lie,false,sizeof(lie));
	memset(small,false,sizeof(small)); 
	for(int i=1;i<=9;i++)
	{
		ss[i].xx=i;
		for(int j=1;j<=9;j++)
		{
			scanf("%d",&a[i][j]);
			small[(i-1)/3+1][(j-1)/3+1][a[i][j]]=true;
			lie[i][a[i][j]]=true; 
			hang[j][a[i][j]]=true; 
			if(a[i][j]==0) ss[i].sl++; 
		}
	}
	sort(ss+1,ss+10,cmp); 
	dfs(1,1);
	if(maxx==0) printf("-1"); 
	else printf("%d",maxx); 
}

感谢FHM大佬的指导,如有不好的请指出

 

请经过博主允许方可转载,谢谢!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值