小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
刚学了DLX闲着没事去试了一下各种90、95的NOIP靶形数独,结果速度确实快了好几倍恩恩。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int MAXC = 324 + 10; 9 const int MAXR = 729 + 10; 10 const int MAXP = MAXR * 4 + MAXC; 11 12 const int p[9][9] = { 13 {6,6,6,6,6,6,6,6,6}, 14 {6,7,7,7,7,7,7,7,6}, 15 {6,7,8,8,8,8,8,7,6}, 16 {6,7,8,9,9,9,8,7,6}, 17 {6,7,8,9,10,9,8,7,6}, 18 {6,7,8,9,9,9,8,7,6}, 19 {6,7,8,8,8,8,8,7,6}, 20 {6,7,7,7,7,7,7,7,6}, 21 {6,6,6,6,6,6,6,6,6} 22 }; 23 24 struct DLX { 25 int n, sz;//列数,结点总数 26 int sum[MAXC];//每列拥有的结点数 27 int row[MAXP], col[MAXP], get[MAXP];//结点所在的行和列 28 int left[MAXP], right[MAXP], up[MAXP], down[MAXP];//十字链表 29 int ans; 30 31 void init(int nn) { 32 n = nn; 33 for(int i = 0; i <= n; ++i) { 34 up[i] = down[i] = i; 35 left[i] = i - 1; right[i] = i + 1; 36 } 37 right[n] = 0; left[0] = n; 38 sz = n + 1; 39 memset(sum, 0, sizeof(sum)); 40 } 41 42 void add_row(int r, int _get, vector<int> columns) { 43 int first = sz; 44 for(int i = 0, len = columns.size(); i < len; ++i) { 45 int c = columns[i]; 46 left[sz] = sz - 1; right[sz] = sz + 1; down[sz] = c; up[sz] = up[c]; 47 down[up[c]] = sz; up[c] = sz; 48 row[sz] = r; col[sz] = c; get[sz] = _get; 49 ++sum[c]; ++sz; 50 } 51 right[sz - 1] = first; left[first] = sz - 1; 52 } 53 54 void remove(int c) { 55 left[right[c]] = left[c]; 56 right[left[c]] = right[c]; 57 for(int i = down[c]; i != c; i = down[i]) 58 for(int j = right[i]; j != i; j = right[j]) { 59 up[down[j]] = up[j]; down[up[j]] = down[j]; --sum[col[j]]; 60 } 61 } 62 63 void restore(int c) { 64 for(int i = up[c]; i != c; i = up[i]) 65 for(int j = left[i]; j != i; j = left[j]) { 66 up[down[j]] = j; down[up[j]] = j; ++sum[col[j]]; 67 } 68 left[right[c]] = c; 69 right[left[c]] = c; 70 } 71 72 void dfs(int d, int score) { 73 if(right[0] == 0) { 74 ans = max(ans, score); 75 return ; 76 } 77 int c = right[0]; 78 for(int i = right[0]; i != 0; i = right[i]) if(sum[i] < sum[c]) c = i; 79 remove(c); 80 for(int i = down[c]; i != c; i = down[i]) { 81 for(int j = right[i]; j != i; j = right[j]) remove(col[j]); 82 dfs(d + 1, score + get[i]); 83 for(int j = left[i]; j != i; j = left[j]) restore(col[j]); 84 } 85 restore(c); 86 } 87 88 int solve() { 89 ans = -1; 90 dfs(0, 0); 91 return ans; 92 } 93 }; 94 95 DLX solver; 96 97 const int SLOT = 0; 98 const int ROW = 1; 99 const int COL = 2; 100 const int SUB = 3; 101 102 inline int encode(int a, int b, int c) { 103 return a * 81 + b * 9 + c + 1; 104 } 105 106 int puzzle[9][9]; 107 108 void read() { 109 for(int i = 0; i < 9; ++i) 110 for(int j = 0; j < 9; ++j) scanf("%d", &puzzle[i][j]); 111 } 112 113 int main() { 114 read(); 115 solver.init(324); 116 for(int r = 0; r < 9; ++r) 117 for(int c = 0; c < 9; ++c) 118 for(int v = 0; v < 9; ++v) 119 if(puzzle[r][c] == 0 || puzzle[r][c] == 1 + v) { 120 vector<int> columns; 121 columns.push_back(encode(SLOT, r, c)); 122 columns.push_back(encode(ROW, r, v)); 123 columns.push_back(encode(COL, c, v)); 124 columns.push_back(encode(SUB, (r/3)*3+c/3, v)); 125 solver.add_row(encode(r, c, v), p[r][c] * (1 + v), columns); 126 } 127 printf("%d\n", solver.solve()); 128 }