noip 2009 靶形数独
题目描述 Description
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他
们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,
Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格
高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些
数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能
重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即
每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。
上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红
色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕
色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的
要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取
更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字
的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游
戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能
够得到的最高分数。
一共 9 行。每行9 个整数(每个数都在0—9 的范围内),表示一个尚未填满的数独方
格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。
输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-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
【输入输出样例 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
【输入输出样例 1】
2829
【输入输出样例 1】
2852
【数据范围】
40%的数据,数独中非0 数的个数不少于30。
80%的数据,数独中非0 数的个数不少于26。
100%的数据,数独中非0 数的个数不少于24。
这道题很不正经。。。先来用个正常点的思维,对于剩下的分数和乘8比当前最优解还小,直接return掉,再加个位运算优化,然后codevs和vijos轻松A掉4000毫秒左右。然后交到教师机去测,竟然T掉。。。这可不科学。。。
Code1
1 #include<iostream> 2 #include<fstream> 3 #include<sstream> 4 #include<cstdio> 5 #include<cstdlib> 6 #include<cstring> 7 #include<ctime> 8 #include<cctype> 9 #include<cmath> 10 #include<algorithm> 11 #include<stack> 12 #include<queue> 13 #include<set> 14 #include<map> 15 #include<vector> 16 using namespace std; 17 typedef bool boolean; 18 #define smin(a, b) (a) = min((a), (b)) 19 #define smax(a, b) (a) = max((a), (b)) 20 template<typename T> 21 inline void readInteger(T& u){ 22 char x; 23 int aFlag = 1; 24 while(!isdigit((x = getchar())) && x != '-'); 25 if(x == '-'){ 26 x = getchar(); 27 aFlag = -1; 28 } 29 for(u = x - '0'; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - '0'); 30 ungetc(x, stdin); 31 u *= aFlag; 32 } 33 34 const int score[9][9] = {{6, 6, 6, 6, 6, 6, 6, 6, 6}, 35 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 36 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 37 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 38 {6, 7, 8, 9, 10,9, 8, 7, 6}, 39 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 40 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 41 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 42 {6, 6, 6, 6, 6, 6, 6, 6, 6}}; 43 int result = -1; 44 short bmat[9][9]; 45 int cells[9]; 46 int lines[9]; 47 int rows[9]; 48 int sum; 49 50 void search(int s, int x, int y){ 51 if(y == 9){ 52 smax(result, s); 53 return; 54 } 55 if(sum * 8 + s <= result) return; 56 if(bmat[x][y] == 0){ 57 int ce = (x / 3) * 3 + y / 3; 58 int seced = (rows[x] | lines[y]) | cells[ce]; 59 for(int i = 8; i >= 0; i--){ 60 if(!(seced & (1 << i))){ 61 int mask = 1 << i; 62 rows[x] |= mask, lines[y] |= mask, cells[ce] |= mask, sum -= i + 1; 63 search(s + (i + 1) * score[x][y], ((x == 8) ? (0) : (x + 1)), 64 ((x == 8) ? (y + 1) : (y))); 65 rows[x] ^= mask, lines[y] ^= mask, cells[ce] ^= mask, sum += i + 1; 66 } 67 } 68 }else{ 69 sum -= bmat[x][y]; 70 search(s + bmat[x][y] * score[x][y], ((x == 8) ? (0) : (x + 1)), 71 ((x == 8) ? (y + 1) : (y))); 72 sum += bmat[x][y]; 73 } 74 } 75 76 inline void init(){ 77 for(int i = 0; i < 9; i++){ 78 for(int j = 0; j < 9; j++){ 79 readInteger(bmat[i][j]); 80 if(bmat[i][j] != 0){ 81 int mask = (1 << (bmat[i][j] - 1)); 82 rows[i] |= mask, lines[j] |= mask, cells[(i / 3) * 3 + j / 3] |= mask; 83 } 84 } 85 } 86 sum = 405; 87 } 88 89 inline void solve(){ 90 search(0, 0, 0); 91 printf("%d", result); 92 } 93 94 int main(){ 95 freopen("sudoku.in", "r", stdin); 96 freopen("sudoku.out", "w", stdout); 97 init(); 98 solve(); 99 return 0; 100 }
教师机跑得比较慢,但是还是要“体谅”一下。然后再换个方法剪枝,改变搜索顺序,先搜约束条件少的
Code2
1 #include<iostream> 2 #include<sstream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<ctime> 7 #include<cctype> 8 #include<cmath> 9 #include<algorithm> 10 #include<stack> 11 #include<queue> 12 #include<set> 13 #include<map> 14 #include<vector> 15 using namespace std; 16 typedef bool boolean; 17 #define smin(a, b) (a) = min((a), (b)) 18 #define smax(a, b) (a) = max((a), (b)) 19 template<typename T> 20 inline void readInteger(T& u){ 21 char x; 22 int aFlag = 1; 23 while(!isdigit((x = getchar())) && x != '-'); 24 if(x == '-'){ 25 x = getchar(); 26 aFlag = -1; 27 } 28 for(u = x - '0'; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - '0'); 29 ungetc(x, stdin); 30 u *= aFlag; 31 } 32 33 typedef class Cell { 34 public: 35 int x, y; 36 int mask; 37 38 Cell(const int x = 0, const int y = 0, const int mask = 0):x(x), y(y), mask(mask) { } 39 40 boolean operator < (Cell b) const { 41 return mask > b.mask; 42 } 43 }Cell; 44 45 const int score[9][9] = {{6, 6, 6, 6, 6, 6, 6, 6, 6}, 46 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 47 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 48 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 49 {6, 7, 8, 9, 10,9, 8, 7, 6}, 50 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 51 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 52 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 53 {6, 6, 6, 6, 6, 6, 6, 6, 6}}; 54 int result = -1; 55 short bmat[9][9]; 56 int cells[9]; 57 int lines[9]; 58 int rows[9]; 59 int sum; 60 Cell table[81]; 61 62 void search(int s, int t){ 63 if(t == 81){ 64 smax(result, s); 65 return; 66 } 67 if(sum * 9 + s <= result) return; 68 int x = table[t].x, y = table[t].y; 69 if(bmat[x][y] == 0){ 70 int ce = (x / 3) * 3 + y / 3; 71 int seced = (rows[x] | lines[y]) | cells[ce]; 72 for(int i = 8; i >= 0; i--){ 73 if(!(seced & (1 << i))){ 74 int mask = 1 << i; 75 rows[x] |= mask, lines[y] |= mask, cells[ce] |= mask, sum -= i + 1; 76 search(s + (i + 1) * score[x][y], t + 1); 77 rows[x] ^= mask, lines[y] ^= mask, cells[ce] ^= mask, sum += i + 1; 78 } 79 } 80 }else{ 81 sum -= bmat[x][y]; 82 search(s + bmat[x][y] * score[x][y], t + 1); 83 sum += bmat[x][y]; 84 } 85 } 86 87 inline void init(){ 88 for(int i = 0; i < 9; i++){ 89 for(int j = 0; j < 9; j++){ 90 readInteger(bmat[i][j]); 91 if(bmat[i][j] != 0){ 92 int mask = (1 << (bmat[i][j] - 1)); 93 rows[i] |= mask, lines[j] |= mask, cells[(i / 3) * 3 + j / 3] |= mask; 94 } 95 } 96 } 97 sum = 405; 98 } 99 100 const int full = (1 << 9) - 1; 101 102 inline void solve(){ 103 for(int i = 0; i < 9; i++) { 104 for(int j = 0; j < 9; j++) { 105 if(bmat[i][j] != 0) 106 table[i * 9 + j] = Cell(i, j, full); 107 else table[i * 9 + j] = Cell(i, j, (rows[i] | lines[j]) | cells[(i / 3) * 3 + j / 3]); 108 } 109 } 110 sort(table, table + 81); 111 search(0, 0); 112 printf("%d", result); 113 } 114 115 int main(){ 116 freopen("sudoku.in", "r", stdin); 117 freopen("sudoku.out", "w", stdout); 118 init(); 119 solve(); 120 return 0; 121 }
于是刚刚卡我的那个点A掉了,但是多T掉了3个点,内心是崩溃的。不行再结合一下之前的搜索顺序
Code3
1 #include<iostream> 2 #include<sstream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<ctime> 7 #include<cctype> 8 #include<cmath> 9 #include<algorithm> 10 #include<stack> 11 #include<queue> 12 #include<set> 13 #include<map> 14 #include<vector> 15 using namespace std; 16 typedef bool boolean; 17 #define smin(a, b) (a) = min((a), (b)) 18 #define smax(a, b) (a) = max((a), (b)) 19 template<typename T> 20 inline void readInteger(T& u){ 21 char x; 22 int aFlag = 1; 23 while(!isdigit((x = getchar())) && x != '-'); 24 if(x == '-'){ 25 x = getchar(); 26 aFlag = -1; 27 } 28 for(u = x - '0'; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - '0'); 29 ungetc(x, stdin); 30 u *= aFlag; 31 } 32 33 typedef class Cell { 34 public: 35 int x, y; 36 int mask; 37 38 Cell(const int x = 0, const int y = 0, const int mask = 0):x(x), y(y), mask(mask) { } 39 40 boolean operator < (Cell b) const { 41 if(x != b.x) return x > b.x; 42 return mask > b.mask; 43 } 44 }Cell; 45 46 const int score[9][9] = {{6, 6, 6, 6, 6, 6, 6, 6, 6}, 47 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 48 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 49 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 50 {6, 7, 8, 9, 10,9, 8, 7, 6}, 51 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 52 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 53 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 54 {6, 6, 6, 6, 6, 6, 6, 6, 6}}; 55 int result = -1; 56 short bmat[9][9]; 57 int cells[9]; 58 int lines[9]; 59 int rows[9]; 60 int sum; 61 Cell table[81]; 62 63 void search(int s, int t){ 64 if(t == 81){ 65 smax(result, s); 66 return; 67 } 68 if(sum * 9 + s <= result) return; 69 int x = table[t].x, y = table[t].y; 70 if(bmat[x][y] == 0){ 71 int ce = (x / 3) * 3 + y / 3; 72 int seced = (rows[x] | lines[y]) | cells[ce]; 73 for(int i = 8; i >= 0; i--){ 74 if(!(seced & (1 << i))){ 75 int mask = 1 << i; 76 rows[x] |= mask, lines[y] |= mask, cells[ce] |= mask, sum -= i + 1; 77 search(s + (i + 1) * score[x][y], t + 1); 78 rows[x] ^= mask, lines[y] ^= mask, cells[ce] ^= mask, sum += i + 1; 79 } 80 } 81 }else{ 82 sum -= bmat[x][y]; 83 search(s + bmat[x][y] * score[x][y], t + 1); 84 sum += bmat[x][y]; 85 } 86 } 87 88 inline void init(){ 89 for(int i = 0; i < 9; i++){ 90 for(int j = 0; j < 9; j++){ 91 readInteger(bmat[i][j]); 92 if(bmat[i][j] != 0){ 93 int mask = (1 << (bmat[i][j] - 1)); 94 rows[i] |= mask, lines[j] |= mask, cells[(i / 3) * 3 + j / 3] |= mask; 95 } 96 } 97 } 98 sum = 405; 99 } 100 101 const int full = (1 << 9) - 1; 102 103 inline void solve(){ 104 for(int i = 0; i < 9; i++) { 105 for(int j = 0; j < 9; j++) { 106 if(bmat[i][j] != 0) 107 table[i * 9 + j] = Cell(i, j, full); 108 else table[i * 9 + j] = Cell(i, j, (rows[i] | lines[j]) | cells[(i / 3) * 3 + j / 3]); 109 } 110 } 111 sort(table, table + 81); 112 search(0, 0); 113 printf("%d", result); 114 } 115 116 int main(){ 117 freopen("sudoku.in", "r", stdin); 118 freopen("sudoku.out", "w", stdout); 119 init(); 120 solve(); 121 return 0; 122 }
OJ上1600ms左右,教师机上20个点终于都过了。
最后发现,倒着搜索,是最简单的AC方法,OJ上2100ms左右
Code4
1 #include<iostream> 2 #include<sstream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<ctime> 7 #include<cctype> 8 #include<cmath> 9 #include<algorithm> 10 #include<stack> 11 #include<queue> 12 #include<set> 13 #include<map> 14 #include<vector> 15 using namespace std; 16 typedef bool boolean; 17 #define smin(a, b) (a) = min((a), (b)) 18 #define smax(a, b) (a) = max((a), (b)) 19 template<typename T> 20 inline void readInteger(T& u){ 21 char x; 22 int aFlag = 1; 23 while(!isdigit((x = getchar())) && x != '-'); 24 if(x == '-'){ 25 x = getchar(); 26 aFlag = -1; 27 } 28 for(u = x - '0'; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - '0'); 29 ungetc(x, stdin); 30 u *= aFlag; 31 } 32 33 typedef class Cell { 34 public: 35 int x, y; 36 int mask; 37 38 Cell(const int x = 0, const int y = 0, const int mask = 0):x(x), y(y), mask(mask) { } 39 40 boolean operator < (Cell b) const { 41 if(x != b.x) return x > b.x; 42 return y > b.y; 43 } 44 }Cell; 45 46 const int score[9][9] = {{6, 6, 6, 6, 6, 6, 6, 6, 6}, 47 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 48 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 49 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 50 {6, 7, 8, 9, 10,9, 8, 7, 6}, 51 {6, 7, 8, 9, 9, 9, 8, 7, 6}, 52 {6, 7, 8, 8, 8, 8, 8, 7, 6}, 53 {6, 7, 7, 7, 7, 7, 7, 7, 6}, 54 {6, 6, 6, 6, 6, 6, 6, 6, 6}}; 55 int result = -1; 56 short bmat[9][9]; 57 int cells[9]; 58 int lines[9]; 59 int rows[9]; 60 int sum; 61 Cell table[81]; 62 63 void search(int s, int t){ 64 if(t == 81){ 65 smax(result, s); 66 return; 67 } 68 if(sum * 9 + s <= result) return; 69 int x = table[t].x, y = table[t].y; 70 if(bmat[x][y] == 0){ 71 int ce = (x / 3) * 3 + y / 3; 72 int seced = (rows[x] | lines[y]) | cells[ce]; 73 for(int i = 8; i >= 0; i--){ 74 if(!(seced & (1 << i))){ 75 int mask = 1 << i; 76 rows[x] |= mask, lines[y] |= mask, cells[ce] |= mask, sum -= i + 1; 77 search(s + (i + 1) * score[x][y], t + 1); 78 rows[x] ^= mask, lines[y] ^= mask, cells[ce] ^= mask, sum += i + 1; 79 } 80 } 81 }else{ 82 sum -= bmat[x][y]; 83 search(s + bmat[x][y] * score[x][y], t + 1); 84 sum += bmat[x][y]; 85 } 86 } 87 88 inline void init(){ 89 for(int i = 0; i < 9; i++){ 90 for(int j = 0; j < 9; j++){ 91 readInteger(bmat[i][j]); 92 if(bmat[i][j] != 0){ 93 int mask = (1 << (bmat[i][j] - 1)); 94 rows[i] |= mask, lines[j] |= mask, cells[(i / 3) * 3 + j / 3] |= mask; 95 } 96 } 97 } 98 sum = 405; 99 } 100 101 const int full = (1 << 9) - 1; 102 103 inline void solve(){ 104 for(int i = 0; i < 9; i++) { 105 for(int j = 0; j < 9; j++) { 106 if(bmat[i][j] != 0) 107 table[i * 9 + j] = Cell(i, j, full); 108 else table[i * 9 + j] = Cell(i, j, (rows[i] | lines[j]) | cells[(i / 3) * 3 + j / 3]); 109 } 110 } 111 sort(table, table + 81); 112 search(0, 0); 113 printf("%d", result); 114 } 115 116 int main(){ 117 freopen("sudoku.in", "r", stdin); 118 freopen("sudoku.out", "w", stdout); 119 init(); 120 solve(); 121 return 0; 122 }