两道数独求解的问题。
思路:
很显然,两道题目的思路都是,读入数据记录所有待求解的点,然后进行深搜即可。
但是不难看出的一点是,如果是单纯的暴力深搜,搜索的深度可能达到81,这样做可能会超时,于是要进行剪枝。
剪枝的方法很简单,预先算出进行下一步扩展的点的个数,然后进行排序,扩展点个数少的先搜索。
由于本题是求解数独,在最初的时候对各个待求解的点根据可能的情况数排序之后确定搜索的先后顺序即可。
方法:
记录每一行每一列每一个数字是否出现,记录所有带求解的点,深搜,剪枝。
1162 剪枝前代码:
#include <cstring>
#include <cstdio>
char square[10][10];
bool row[10][10], col[10][10], blo[10][10];
int x[85], y[85], todo, flag;
int block(int i, int j) {return (i-1)/3*3 + (j-1)/3 + 1;}
void dfs(int n)
{
if (flag || n == todo) { flag = 1; return; }
int xx = x[n], yy = y[n], bl = block(xx,yy);
for (int temp = 1; temp <= 9; ++ temp)
{
if (!row[xx][temp] && !col[yy][temp] && !blo[bl][temp])
{
square[xx][yy] = temp+'0';
row[xx][temp] = 1; col[yy][temp] = 1; blo[bl][temp] = 1;
dfs(n+1); if (flag) return;
row[xx][temp] = 0; col[yy][temp] = 0; blo[bl][temp] = 0;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t --)
{
flag = 0; todo = 0;
memset(row, false , sizeof(row));
memset(col, false , sizeof(col));
memset(blo, false , sizeof(blo));
for (int i = 1; i <= 9; ++ i)
{
scanf("%s", square[i]+1);
for (int j = 1; j <= 9; ++ j)
{
if (square[i][j] == '0')
{
x[todo] = i;
y[todo] = j;
todo ++;
}
else
{
row[i][square[i][j]-'0'] = 1;
col[j][square[i][j]-'0'] = 1;
blo[block(i,j)][square[i][j]-'0'] = 1;
}
}
}
dfs(0);
for (int i = 1; i <= 9; ++ i)
printf("%s\n", square[i]+1);
}
}
结果:Accepted 0.35s
剪枝后代码:
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
char square[10][10];
bool row[10][10], col[10][10], blo[10][10];
int todo, flag;
int block(int i, int j) {return (i-1)/3*3 + (j-1)/3 + 1;}
struct TODO
{
int x, y, b, pNum;
TODO() {}
TODO(int _x, int _y): x(_x), y(_y), b(block(_x,_y)), pNum() {}
void calc()
{
for (int i = 1; i <= 9; ++ i)
if (!row[x][i] && !col[y][i] && !blo[b][i])
pNum ++;
}
};
bool cmp(const TODO &a, const TODO &b) {return a.pNum < b.pNum;}
TODO p[85];
void dfs(int n)
{
if (flag || n == todo) { flag = 1; return; }
int xx = p[n].x, yy = p[n].y, bl = p[n].b;
for (int temp = 1; temp <= 9; ++ temp)
{
if (!row[xx][temp] && !col[yy][temp] && !blo[bl][temp])
{
square[xx][yy] = temp+'0';
row[xx][temp] = 1; col[yy][temp] = 1; blo[bl][temp] = 1;
dfs(n+1); if (flag) return;
row[xx][temp] = 0; col[yy][temp] = 0; blo[bl][temp] = 0;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t --)
{
flag = 0; todo = 0;
memset(row, false , sizeof(row));
memset(col, false , sizeof(col));
memset(blo, false , sizeof(blo));
for (int i = 1; i <= 9; ++ i)
{
scanf("%s", square[i]+1);
for (int j = 1; j <= 9; ++ j)
{
if (square[i][j] == '0')
p[todo++] = TODO(i,j);
else
{
row[i][square[i][j]-'0'] = 1;
col[j][square[i][j]-'0'] = 1;
blo[block(i,j)][square[i][j]-'0'] = 1;
}
}
}
for (int i = 0; i < todo; ++ i) p[i].calc();
sort(p, p+todo, cmp);
dfs(0);
for (int i = 1; i <= 9; ++ i)
printf("%s\n", square[i]+1);
}
}
结果:Accepted 0.02s
1317剪枝前代码:
#include <cstring>
#include <cstdio>
#define N 15
char square[N][N];
bool row[N][N], col[N][N], blo[N][N];
int x[85], y[85], ans[N][N], todo, num;
inline int block(int i, int j) {return (i-1)/3*3 + (j-1)/3 + 1;}
void dfs(int n)
{
if (n == todo)
{
num ++;
if (num == 1)
for (int i = 1; i <= 9; ++ i)
for (int j = 1; j <= 9; ++ j)
ans[i][j] = square[i][j]-'0';
return;
}
int xx = x[n], yy = y[n], bl = block(xx,yy);
for (int temp = 1; temp <= 9; ++ temp)
{
if (!row[xx][temp] && !col[yy][temp] && !blo[bl][temp])
{
square[xx][yy] = temp+'0';
row[xx][temp] = 1; col[yy][temp] = 1; blo[bl][temp] = 1;
dfs(n+1);
row[xx][temp] = 0; col[yy][temp] = 0; blo[bl][temp] = 0;
}
}
}
int main()
{
int t;
scanf("%d", &t);
for (int Case = 1; Case <= t; ++ Case)
{
if (Case != 1) printf("\n");
num = 0; todo = 0;
memset(row, false , sizeof(row));
memset(col, false , sizeof(col));
memset(blo, false , sizeof(blo));
for (int i = 1; i <= 9; ++ i)
{
scanf("%s", square[i]+1);
for (int j = 1; j <= 9; ++ j)
{
if (square[i][j] == '_')
{
x[todo] = i;
y[todo] = j;
todo += 1;
}
else
{
row[i][square[i][j]-'0'] = 1;
col[j][square[i][j]-'0'] = 1;
blo[block(i,j)][square[i][j]-'0'] = 1;
}
}
}
dfs(0);
if (num == 0) printf("Puzzle %d has no solution\n", Case);
else if (num == 1)
{
printf("Puzzle %d solution is\n", Case);
for (int i = 1; i <= 9; ++ i)
{
for (int j = 1; j <= 9; ++ j)
printf("%d", ans[i][j]);
printf("\n");
}
}
else printf("Puzzle %d has %d solutions\n", Case, num);
}
}
结果:Time Limit Exceeded
剪枝后代码:
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 12
char square[N][N], ans[N][N];
bool row[N][N], col[N][N], blo[N][N];
int todo, num;
int block(int i, int j) {return (i-1)/3*3 + (j-1)/3 + 1;}
struct TODO
{
int x, y, b, pNum;
TODO() {}
TODO(int _x, int _y): x(_x), y(_y), b(block(_x,_y)), pNum(0) {}
void calc()
{
for (int i = 1; i <= 9; ++ i)
if (!row[x][i] && !col[y][i] && !blo[b][i])
pNum ++;
}
};
bool cmp(const TODO &a, const TODO &b) {return a.pNum < b.pNum;}
TODO p[85];
void dfs(int n)
{
if (n == todo)
{
num ++;
if (num == 1) memcpy(ans, square, sizeof(square));
return;
}
int xx = p[n].x, yy = p[n].y, bl = p[n].b;
for (int temp = 1; temp <= 9; ++ temp)
{
if (!row[xx][temp] && !col[yy][temp] && !blo[bl][temp])
{
square[xx][yy] = temp+'0';
row[xx][temp] = 1; col[yy][temp] = 1; blo[bl][temp] = 1;
dfs(n+1);
row[xx][temp] = 0; col[yy][temp] = 0; blo[bl][temp] = 0;
}
}
}
int main()
{
int t;
scanf("%d", &t);
for (int Case = 1; Case <= t; ++ Case)
{
if (Case != 1) printf("\n");
num = 0; todo = 0;
memset(row, false , sizeof(row));
memset(col, false , sizeof(col));
memset(blo, false , sizeof(blo));
for (int i = 1; i <= 9; ++ i)
{
scanf("%s", square[i]+1);
for (int j = 1; j <= 9; ++ j)
{
if (square[i][j] == '_')
p[todo++] = TODO(i,j);
else
{
row[i][square[i][j]-'0'] = 1;
col[j][square[i][j]-'0'] = 1;
blo[block(i,j)][square[i][j]-'0'] = 1;
}
}
}
for (int i = 0; i < todo; ++ i) p[i].calc();
sort(p, p+todo, cmp);
dfs(0);
if (num == 0) printf("Puzzle %d has no solution\n", Case);
else if (num == 1)
{
printf("Puzzle %d solution is\n", Case);
for (int i = 1; i <= 9; ++ i)
printf("%s\n", ans[i]+1);
}
else printf("Puzzle %d has %d solutions\n", Case, num);
}
}
结果:Accepted 0.3s