1858:【09NOIP提高组】靶形数独

【题目描述】

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。

靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)

上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游戏规定,将以总分数的高低决出胜负。

由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。

【输入】

一共 9 行。每行9 个整数(每个数都在0—9 的范围内),表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

【输出】

共1 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。

【输入样例】

7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2

【输出样例】

2829

【提示】

【输入输出样例 2】

输出:

0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6

输出:

2852

【数据范围】

40%的数据,数独中非0 数的个数不少于30。

80%的数据,数独中非0 数的个数不少于26。

100%的数据,数独中非0 数的个数不少于24。

【分析】

第一次:

从第一个一直往下搜,找到分值最大的。

结果:40分  TLE了

第二次:

从每一行每一列最多的开始搜索,找出分值最大的。

结果:75分  还有5个测试点TLE了

第三次:

从每一行每一列最多的开始搜索,找出分值最大的。

在上面的搜索中,是把这在填的数a[x][y]一直从1~9试一遍,1~9每次都判断一次是否可以填这个数。所以这个代码中判断检查其实是非常耗时间的,可以建立一个数组cando[][](值为0表示可以填,为1表示不能填)。然后检查这行的数和这列的数和这个九宫格的数(假设是第n行第m列),如果这个数不为0,则这个数不能填,将c[a[n][m]]=1。检查好后再进行1~9的循环查找:如果这个数c[i]=1那么就continue,进行第i+1数的查找;否则搜索下一个数。

AC代码如下:

#include<bits/stdc++.h>//数独
using namespace std;
int a[11][11];
int b[11];//每行已知数最多
int bid[11];//b的id
int bbid;//bid第几个
int c[11];//每列已知数最多
int cid[11];//c的id
int ccid;//cid第几个
int maxx;//最大分值
int cando[1001][11];//搜索中判断能填什么数
int candoid=1;//第几个
bool fff=false;//是否有解
void checkx(int x) {//判断第几行
	for(int i=1; i<=9; i++) {
		if(a[x][i]==0)
			continue;
		cando[candoid][a[x][i]]=1;
	}
}
void checky(int y) { //判断第几列
	for(int i=1; i<=9; i++) {
		if(a[i][y]==0)
			continue;
		cando[candoid][a[i][y]]=1;
	}
}
void jggfz(int djgg) {//将第几个九宫格变为起始点(x,y),进行九宫格的判断
	int x,y;
	if(djgg==1)
		x=1,y=1;
	else if(djgg==2)
		x=1,y=4;
	else if(djgg==3)
		x=1,y=7;
	else if(djgg==4)
		x=4,y=1;
	else if(djgg==5)
		x=4,y=4;
	else if(djgg==6)
		x=4,y=7;
	else if(djgg==7)
		x=7,y=1;
	else if(djgg==8)
		x=7,y=4;
	else
		x=7,y=7;
	for(int k=x; k<x+3; k++) {
		for(int i=y; i<y+3; i++) {
			if(a[k][i]==0)
				continue;
			cando[candoid][a[k][i]]=1;
		}
	}
}
int fzfz(int x) { //辅助分值计算
	if(x==5)
		return 10*a[5][5];
	int sum=0;
	for(int i=x; i<=10-x; i++)
		sum+=(5+x)*a[x][i];
	for(int i=x+1; i<=10-(x+1); i++)
		sum+=(5+x)*a[i][x];
	for(int i=x+1; i<=10-(x+1); i++)
		sum+=(5+x)*a[i][10-x];
	for(int i=x; i<=10-x; i++)
		sum+=(5+x)*a[10-x][i];
	return sum;
}
int fz() { //分值的计算
	int sum=0;
	for(int i=1; i<=5; i++)
		sum+=fzfz(i);
	return sum;
}
void dfs(int na,int nb) { //第几行第几个
	if(bbid==10) {
		fff=true;
		int sum=fz();
		if(sum>=maxx)
			maxx=sum;
		return;
	}
	if(a[na][nb]!=0) {
		int bbidd=bbid,ccidd=ccid;
		if(ccid==9)
			bbid++,ccid=1;
		else
			ccid++;
		dfs(bid[bbid],cid[ccid]);
		bbid=bbidd,ccid=ccidd;
		return;
	}
	//只判断一次,告诉能填什么数
	for(int i=1;i<=9;i++)
		cando[candoid][i]=0;
	int djgg=(na-1)/3*3+(nb-1)/3+1;//用公式算出第x,y是第几个九宫格
	checkx(na);
	checky(nb);
	jggfz(djgg);
	for(int i=1; i<=9; i++) {
		if(cando[candoid][i]==1)
			continue;
		a[na][nb]=i;
		int bbidd=bbid,ccidd=ccid;
		if(ccid==9)
			bbid++,ccid=1;
		else
			ccid++;
		candoid++;
		dfs(bid[bbid],cid[ccid]);
		candoid--;
		bbid=bbidd,ccid=ccidd;
		a[na][nb]=0;
	}
}
int main() {
	for(int i=1; i<=9; i++) {
		for(int j=1; j<=9; j++) {
			scanf("%d",&a[i][j]);
			if(a[i][j]!=0)
				b[i]++;
			if(a[j][i]!=0)
				c[i]++;
		}
	}
	//哪行已知数最多从哪行开始填,哪列已知数多从那列开始填
	for(int i=1; i<=9; i++)
		bid[i]=i,cid[i]=i;
	for(int i=1; i<=9; i++) {
		for(int j=1; j<=9-i; j++) {
			if(b[j]<b[j+1])
				swap(b[j],b[j+1]),swap(bid[j],bid[j+1]);
			if(c[j]<c[j+1])
				swap(c[j],c[j+1]),swap(cid[j],cid[j+1]);
		}
	}
	dfs(bid[++bbid],cid[++ccid]);
	if(fff==false)
		printf("-1");
	else
		printf("%d",maxx);
	return 0;
}

代码写得很长,运行时间也比较慢,但比较好理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值