洛谷 P1074 靶形数独(剪枝)
//人生中第一道蓝题(3.5h)
题目描述
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个 3 格宽3×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为9分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕色区域)每个格子为7分,最外面一圈(白色区域)每个格子为6分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和
总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
输入输出格式
输入格式:
一共 9 行。每行9个整数(每个数都在 0−9 的范围内),表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。
输出格式:
输出共 1 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。
输入输出样例
说明
【数据范围】
40%的数据,数独中非 00 数的个数不少于3030。
80%的数据,数独中非 00 数的个数不少于2626。
100%的数据,数独中非00数的个数不少于2424。
NOIP 2009 提高组 第四题
解题思路:
搜索,用一个数组s保存要存的点,其中s[ i ][0]与s[ i ][1]存点的坐标,s[ i ][2]存点在什么颜色上,s[ i ][3]存点的所在宫,用三个布尔数组分别记录当前行、列、宫中已经放了哪些数,如果这样搜会TLE,所以我们要剪枝,因为dfs层数与0的个数有关,层数太多就TLE了,我们知道,一行中填过的数字越多,需要填的数越少,就意味着dfs层数越少!所以,我们先填0的数量少的行。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 struct kkk { 5 int rank,sum = 0;//rank表示行号,sum表示这行0的个数 6 }e[10]; 7 int a[10][10],s[100][4],bj = 1,ans,daan = -1;//bj记录0的个数,daan为最终答案 8 bool h[10][10],l[10][10],g[10][10];//h,l,g分别表示行列宫的放置情况 9 int find(int i,int j) {//找当前点在哪个宫 10 if(i <= 3) { 11 if(j <= 3) return 1; 12 else if(j <= 6) return 2; 13 else return 3; 14 } 15 else if(i <= 6) { 16 if(j <= 3) return 4; 17 else if(j <= 6) return 5; 18 else return 6; 19 } 20 else { 21 if(j <= 3) return 7; 22 else if(j <= 6) return 8; 23 else return 9; 24 } 25 } 26 int score(int i,int j) {//判断当前点在哪个颜色上 27 if(i == 1 || j == 1 || i == 9 || j == 9) return 6; 28 if(i == 2 || j == 2 || i == 8 || j == 8) return 7; 29 if(i == 3 || j == 3 || i == 7 || j == 7) return 8; 30 if(i == 4 || j == 4 || i == 6 || j == 6) return 9; 31 return 10; 32 } 33 bool cmp(kkk d,kkk b) {//根据每行0的个数排序 34 return d.sum < b.sum; 35 } 36 void dfs(int b,int k) {//表示在搜s[b],当前分数为k 37 if(b == bj) {//合法填完了所有数 38 if(k > daan) daan = k;//更新答案 39 return ; 40 } 41 for(int i = 1;i <= 9; i++) { 42 if(!h[s[b][0]][i] && !l[s[b][1]][i] && !g[s[b][3]][i]) {//如果当前行,列,宫没有放过这个数 43 h[s[b][0]][i] = l[s[b][1]][i] = g[s[b][3]][i] = 1;//标记 44 dfs(b + 1,k + (s[b][2] * i));//搜索 45 h[s[b][0]][i] = l[s[b][1]][i] = g[s[b][3]][i] = 0;//回溯 46 } 47 } 48 return ; 49 } 50 int main() 51 { 52 for(int i = 1;i <= 9; i++) e[i].rank = i;//初始化行号 53 for(int i = 1;i <= 9; i++) 54 for(int j = 1;j <= 9; j++) { 55 scanf("%d",&a[i][j]); 56 if(a[i][j] > 0){//如果这个数不是0 57 h[i][a[i][j]] = l[j][a[i][j]] = g[find(i,j)][a[i][j]] = 1;//标记当前行,列,宫已经放置了这个数,以后就不能放这个数了 58 ans += a[i][j] * score(i,j);//ans记录初始化地图已知数字总分和 59 } 60 else e[i].sum++;//否则这行0个数加一 61 } 62 sort(e+1,e+10,cmp); 63 for(int i = 1;i <= 9; i++) 64 for(int j = 1;j <= 9; j++)//从0的个数少的行开始 65 if(a[e[i].rank][j] == 0) 66 s[bj][0] = e[i].rank,s[bj][1] = j,s[bj][2] = score(e[i].rank,j),s[bj++][3] = find(e[i].rank,j); //记录图中0的信息 67 dfs(1,ans); 68 printf("%d",daan); 69 return 0; 70 }
//NOIP提高 2009 T4