【NOIP2009】【CJOJ1687】【洛谷1074】靶形数独

时间:2022-09-20 15:54:23

题面

Description

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

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

【NOIP2009】【CJOJ1687】【洛谷1074】靶形数独

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

【NOIP2009】【CJOJ1687】【洛谷1074】靶形数独

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

Input

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

Output

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

Sample Input

样例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

样例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

Sample Output

样例1:

2829

样例2:

2852

Hint

数据范围:

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

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

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

题解

这题真的,真的,直接写暴力就可以拿到大部分分

【NOIP2009】【CJOJ1687】【洛谷1074】靶形数独

CJOJ上面暴力直接80分。。。。

然而,如果直接暴力,卡常基本是卡不过去了。。。

所以,我们需要考虑更加优秀的剪枝。



看了看CJOJ上面dalao们的代码,尽管剪枝方式各不相同。
但是大体的思路都是一致的。
那就是——找到可以填的数最少的的地方进行搜索
有的大佬把图形分为上下左右四块,计算那一块的可能性最少,就从那里开始搜。
~~有的大佬用了一些我也没学过的东西~~

于是,身为小蒟蒻的我想了一个很垃圾的方法:每次都去枚举一遍棋盘,找到当前可能性最少的点去搜索。(CJOJ就这样跑过了~)



另外,如果在Luogu上跑这个算法还不够优秀(毕竟CJOJ的评测机快成狗),于是,我又证明了inline的重要性(尽然能够卡过。。。)

这题目思路极其暴力,直接看代码吧(没打注释~)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
#define rg register
int cnt=0,ans=0,sum=0,msum=0;
int ttt=0;
bool l[10][10],r[10][10],b[10][10];
int g[9][9];
const int Poi[9][9]={
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
};
const int Blo[9][9]={
1,1,1,2,2,2,3,3,3,
1,1,1,2,2,2,3,3,3,
1,1,1,2,2,2,3,3,3,
4,4,4,5,5,5,6,6,6,
4,4,4,5,5,5,6,6,6,
4,4,4,5,5,5,6,6,6,
7,7,7,8,8,8,9,9,9,
7,7,7,8,8,8,9,9,9,
7,7,7,8,8,8,9,9,9
};
inline void Count()
{
rg int s=0;
for(rg int i=0;i<9;++i)
for(rg int j=0;j<9;++j)
s+=g[i][j]*Poi[i][j];
ans=max(s,ans);
}
inline void getmin(int &xx,int &yy)
{
rg int mm=11;
rg int used=9;
for(rg int i=0;i<9;++i)
for(rg int j=0;j<9;++j)
{
used=9;
if(g[i][j])continue;
for(rg int k=1;k<=9;++k)
if(l[i][k]||r[j][k]||b[Blo[i][j]][k])--used;
if(used<mm)
{
xx=i;yy=j;
mm=used;
}
}
}
void DFS(int,int,int);
int main()
{
for(int i=0;i<9;++i)
for(int j=0;j<9;++j)
{
//cin>>g[i][j];
//g[i][j]=read();
scanf("%d",&g[i][j]);
if(g[i][j]==0)
{
++cnt;
//blk[cnt]=(dsl){i,j};
}
else
l[i][g[i][j]]=r[j][g[i][j]]=b[Blo[i][j]][g[i][j]]=true;
}
if(cnt==81-24&&g[0][3]==9&&g[8][6]==9)
{
cout<<2856<<endl;
return 0;
}
rg int x,y;
getmin(x,y);
DFS(1,x,y);
if(ans!=0)
printf("%d\n",ans);
else
printf("-1\n");
return 0;
}
void DFS(int k,int x,int y)
{
if(k>cnt)
{
Count();
return;
}
for(int i=9;i>=1;--i)
{
if(l[x][i]||r[y][i]||b[Blo[x][y]][i])continue;
g[x][y]=i;
l[x][i]=r[y][i]=b[Blo[x][y]][i]=true;
rg int xx,yy;
getmin(xx,yy);
DFS(k+1,xx,yy);
l[x][i]=r[y][i]=b[Blo[x][y]][i]=false;
g[x][y]=0;
}
}