题目描述
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 99 格宽×99 格高的大九宫格中有99 个 33 格宽×33 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 11 到 99的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 1010 分,黄色区域外面的一圈(红色区域)每个格子为99分,再外面一圈(蓝色区域)每个格子为88 分,蓝色区域外面一圈(棕色区域)每个格子为77分,最外面一圈(白色区域)每个格子为66分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和
总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
输入格式
一共 99 行。每行99个整数(每个数都在 0-90−9 的范围内),表示一个尚未填满的数独方格,未填的空格用“00”表示。每两个数字之间用一个空格隔开。
输出格式
输出共 11 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1−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
输出 #1复制
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
输出 #2复制
2852
说明/提示
【数据范围】
40%的数据,数独中非 00 数的个数不少于3030。
80%的数据,数独中非 00 数的个数不少于2626。
100%的数据,数独中非00数的个数不少于2424。
NOIP 2009 提高组 第四题
解题思路
要了解数独的玩法,(从已知数最多的那行开始填)。所以要对每行零的个数从小到大排序。要定义三个二维数组来判断行列里面是不是已经有那个数了。
代码
#include <bits/stdc++.h>
using namespace std;
int score[10][10]= //靶子上分数的记录
{{0,0,0,0,0,0,0,0,0,0},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6}};
int ninth( int i , int j ) //判断点在哪个九宫格内
{
if( i <= 3 && j <= 3 ) return 1 ;
if( i <= 3 && j <= 6 ) return 2 ;
if( i <= 3 ) return 3 ;
if( i <= 6 && j <= 3 ) return 4 ;
if( i <= 6 && j <= 6 ) return 5 ;
if( i <= 6 ) return 6 ;
if( j <= 3 ) return 7 ;
if( j <= 6 ) return 8 ;
return 9 ;
}
int map1[15][15]; //用来存放数独
struct node
{
int line,s;
}sum[10]; //分别存每行0的个数和行号
bool cmp(node a,node b)
{
return a.s<b.s;
}
bool row[10][10],cow[10][10],f[10][10];
long long ans=-1;//如果不能填输出-1;
long long tongj() //统计分数
{
long long sum = 0 ;
for( int i = 1 ; i <= 9 ; i++ ) {
for( int j = 1 ; j <= 9 ; j++) {
sum=sum+(map1[i][j]*score[i][j]);
}
}
///printf("%lld\n",sum);
return sum;
}
void dfs(int h,int x,int y)
{
/// printf("%d %d %d\n",x,y,h);
if(h==10) //当9行都遍历完了计算分数
{
ans=max(tongj(),ans);
///printf("%lld\n",ans);
return ;
}
if(y==10)//一行填完填下一行
{
dfs(h+1,sum[h+1].line,1);
return ;
}
if(!map1[x][y])//如果该点没被填数
{
for(int i=1;i<=9;i++)
{
if(row[x][i]==0&&cow[y][i]==0&&f[ninth(x,y)][i]==0)//判断该点填i是否合法
{
/// printf("%d %d %d\n",x,y,i);
row[x][i]=1;
cow[y][i]=1;
f[ninth(x,y)][i]=1;
map1[x][y]=i;
dfs(h,x,y+1);
map1[x][y]=0; //回溯
row[x][i]=0;
cow[y][i]=0;
f[ninth(x,y)][i]=0;
}
}
}
else dfs(h,x,y+1);//如果填数了直接填下一个数
}
int main()
{
for(int i=1;i<=9;i++)
{
int k=0;
for(int j=1;j<=9;j++)
{
scanf("%d",&map1[i][j]);
if(!map1[i][j])
k++;
else
{
row[i][map1[i][j]]=1;
cow[j][map1[i][j]]=1;
f[ninth(i,j)][map1[i][j]]=1;
}
//printf("%d ",map1[i][j]);
}
//printf("\n");
sum[i].line=i;
sum[i].s=k;
}
sort(sum+1,sum+10,cmp);
///for(int i=1;i<=9;i++)
/// printf("%d\n",sum[i].line);
dfs(1,sum[1].line,1);
printf("%lld\n",ans);
}