NOIP2009T4 靶形数独
描述
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他
们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,
Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格
高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9 的数字。每个数字在每个小九宫格内不能
重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即
每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红
色区域)每个格子为 9 分,再外面一圈(蓝色区域)每个格子为 8 分,蓝色区域外面一圈(棕
色区域)每个格子为 7 分,最外面一圈(白色区域)每个格子为 6 分,如上图所示。比赛的
要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取
更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字
的乘积的总和
总分数即每个方格上的分值和完成这个数独时填在相应格上的数字
的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能
够得到的最高分数。
输入
一共 9 行。每行 9 个整数(每个数都在 0―9 的范围内),表示一个尚未填满的数独方
格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。
输出
输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-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
sudoku2
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
提示
【数据范围】
40%的数据,数独中非 0 数的个数不少于 30。
80%的数据,数独中非 0 数的个数不少于 26。
100%的数据,数独中非 0 数的个数不少于 24。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int g[10][10],hang[10][10],lie[10][10],gezi[10][10],maxn=0,now; 5 struct node{ 6 int x,y; 7 }; 8 int chang[9][9]; 9 int get(int x,int y){//计算这个格子在哪个九宫格里面 10 int h=(x-1)/3+1; 11 int l=(y-1)/3+1; 12 return (h-1)*3+l; 13 } 14 void dfs(int s){ 15 if(s==81){ 16 //cout<<endl; 17 int ans=0; 18 for(int i=1;i<=9;i++){ 19 for(int j=1;j<=9;j++){ 20 // cout<<g[i][j]<<" "; 21 ans+=g[i][j]*chang[i][j]; 22 } 23 // cout<<endl; 24 } 25 //cout<<ans; 26 //cout<<endl; 27 maxn=max(maxn,ans); 28 return; 29 } 30 int min0=100,ggg,hhh,lll; 31 for(int i=1;i<=9;i++){ 32 for(int j=1;j<=9;j++){//这个使用来找选择数最小的格子 33 if(g[i][j])continue;//填过就跳过 34 int times=0; 35 for(int k=1;k<=9;k++){//找出能填上的个数 36 if(!hang[i][k]&&!lie[j][k]&&!gezi[get(i,j)][k])times++; 37 } 38 if(times<min0){//进行更新并且记录标号 39 if(times==0)return; 40 min0=times; 41 hhh=i,lll=j,ggg=get(i,j); 42 } 43 } 44 } 45 for(int i=1;i<=9;i++){//开始填每个能够填取的数字 46 if(hang[hhh][i]||lie[lll][i]||gezi[get(hhh,lll)][i])continue; 47 g[hhh][lll]=i; 48 hang[hhh][i]=1; 49 lie[lll][i]=1; 50 gezi[get(hhh,lll)][i]=1; 51 dfs(s+1); 52 g[hhh][lll]=0;//记住要恢复现场 53 hang[hhh][i]=0; 54 lie[lll][i]=0; 55 gezi[get(hhh,lll)][i]=0; 56 } 57 } 58 int main(){ 59 // freopen("sudoku.in","r",stdin); 60 // freopen("sudoku.out","w",stdout); 61 //一开始预处理系数 62 for(int i=1;i<=9;i++) 63 for(int j=1;j<=9;j++)chang[i][j]=6; 64 for(int i=2;i<=8;i++) 65 for(int j=2;j<=8;j++)chang[i][j]=7; 66 for(int i=3;i<=7;i++) 67 for(int j=3;j<=7;j++)chang[i][j]=8; 68 for(int i=4;i<=6;i++) 69 for(int j=4;j<=6;j++)chang[i][j]=9; 70 chang[5][5]=10; 71 for(int i=1;i<=9;i++){ 72 for(int j=1;j<=9;j++){ 73 scanf("%d",&g[i][j]); 74 if(g[i][j]){ 75 hang[i][g[i][j]]=1; 76 lie[j][g[i][j]]=1; 77 gezi[get(i,j)][g[i][j]]=1; 78 now++; 79 } 80 } 81 } 82 dfs(now); 83 if(maxn==0)puts("-1"); 84 else 85 printf("%d\n",maxn); 86 return 0; 87 }