1 条题解
-
0
#include<bits/stdc++.h> using namespace std; const int N = 16; int ones[1 << N], mp[1 << N]; int state[N][N]; //state[x][y] 表示,第x行,第y列可以填哪些数字 (0-15) char str[N + 2][N + 2]; int state1[N*N + 1][N][N], state2[N*N + 1][N][N]; char str1[N*N + 1][N + 2][N + 2]; int lowbit(int x) { return x & -x; } void fill(int x, int y, int c) { str[x][y] = 'A' + c; for(int i = 0; i < N; i++) { state[x][i] = state[x][i] & (~(1 << c)); state[i][y] = state[i][y] & (~(1 << c)); } int nx = x / 4 * 4, ny = y / 4 * 4; //nx ny 表示(x,y)所在十六宫格左上角的下标 for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) state[nx + i][ny + j] &= ~(1 << c); state[x][y] = 1 << c; } bool dfs(int cnt) { if(cnt == 0) return true; //找到方案 int cnt1 = cnt; memcpy(state1[cnt1], state, sizeof state); //将state拷贝一份,便于还原 memcpy(str1[cnt1], str, sizeof str); //剪枝1:如果某个空格 不能填任何数字了,就返回, 如果只能填1个数字,就填了 for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) { if(str[i][j] == '-') { if(state[i][j] == 0) { memcpy(state, state1[cnt1], sizeof state); memcpy(str, str1[cnt1], sizeof str); return false; } if(ones[state[i][j]] == 1) { fill(i, j, mp[state[i][j]]); cnt--; } } } // 剪枝2:对于每一行,如果某个数字不能填,则返回false,如果只能填一个数,就填了 for(int i = 0; i < N; i++) { int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填 int c = 0; for(int j = 0; j < N; j++) { int s = state[i][j]; b &= ~(a & s); //把不能填的数字删掉 a |= s; //把所有目前出现的1都并在一个数字里 if(str[i][j] != '-') //把所有的填过数字的格子记录下来 c |= state[i][j]; } if(a != (1 << N) - 1) //填补了0-15 { memcpy(state, state1[cnt1], sizeof state); memcpy(str, str1[cnt1], sizeof str); return false; } for(int j = b; j; j -= lowbit(j)) { int s = lowbit(j); if(!(c & s)) { for(int k = 0; k < N; k++) if(state[i][k] & s) { fill(i, k, mp[s]); cnt --; break; } } } } // // //剪枝3, 对于每一列 for(int i = 0; i < N; i++) { int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填 int c = 0; for(int j = 0; j < N; j++) { int s = state[j][i]; b &= ~(a & s); //把不能填的数字删掉 a |= s; //把所有目前出现的1都并在一个数字里 if(str[j][i] != '-') //把所有的填过数字的格子记录下来 c |= state[j][i]; } if(a != (1 << N) - 1) //填不了0-15 { memcpy(state, state1[cnt1], sizeof state); memcpy(str, str1[cnt1], sizeof str); return false; } for(int j = b; j; j -= lowbit(j)) { int s = lowbit(j); if(!(c & s)) { for(int k = 0; k < N; k++) if(state[k][i] & s) { fill(k, i, mp[s]); cnt --; break; } } } } // // //剪枝4 十六宫格 for(int i = 0; i < N; i++) { int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填 int c = 0; for(int j = 0; j < N; j++) //枚举十六宫格的每个位置 { int nx = i / 4 * 4, ny = i % 4 * 4; int dx = j / 4, dy = j % 4; int s = state[nx + dx][ny + dy]; b &= ~(a & s); //把不能填的数字删掉 a |= s; //把所有目前出现的1都并在一个数字里 if(str[nx + dx][ny + dy] != '-') //把所有的填过数字的格子记录下来 c |= state[nx + dx][ny + dy]; } if(a != (1 << N) - 1) //填不了0-15 { memcpy(state, state1[cnt1], sizeof state); memcpy(str, str1[cnt1], sizeof str); return false; } for(int j = b; j; j -= lowbit(j)) { int s = lowbit(j); if(!(c & s)) { for(int k = 0; k < N; k++) { int nx = i / 4 * 4, ny = i % 4 * 4; int dx = k / 4, dy = k % 4; if(state[nx + dx][ny + dy] & s) { fill(nx + dx, ny + dy, mp[s]); cnt --; break; } } } } } if(cnt == 0) return true; int x, y, s = 100; for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) if(str[i][j] == '-' && ones[state[i][j]] < s) { s = ones[state[i][j]]; x = i, y = j; } memcpy(state2[cnt1], state, sizeof state); for(int i = state[x][y]; i; i -= lowbit(i)) { memcpy(state, state2[cnt1], sizeof state); fill(x, y, mp[lowbit(i)]); if(dfs(cnt - 1)) return true; } //恢复现场 memcpy(state, state1[cnt1], sizeof state); memcpy(str, str1[cnt1], sizeof str); return false; } int main() { for(int i = 0; i < N; i++) mp[1 << i] = i; for(int i = 0; i < (1 << N); i++) for(int j = i; j; j -= lowbit(j)) ones[i] ++; while(cin >> str[0]) { for(int i = 1; i < N; i++) cin >> str[i]; for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) state[i][j] = (1 << 16) - 1; int cnt = 0; //空格的个数 for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) if(str[i][j] != '-') fill(i, j, str[i][j] - 'A'); else cnt ++; dfs(cnt); for(int i = 0; i < N; i++) cout << str[i] << endl; cout << endl; } return 0; } /* 枚举每一个空格,枚举空格能填的数字,不断递归 剪枝: 1.空格能填数字少的优先枚举 2.如果某个空格,不能填任何数字,直接返回。 3.对于每一行,每一列、每一个16宫格,如果存在填不了的情况,则直接返回,否则每个数字只能填一次。 ones[i] 表示二进制数i中1的个数 mp[1 << i] = i; 代表1在第i位,数字i能够使用(未被填过) */
- 1
信息
- ID
- 103
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 12
- 已通过
- 4
- 上传者