您的位置:首页 > 其它

POJ 3074 解题报告

2015-10-06 08:16 351 查看
这道题也是数独题。做完了2676后想把这道题也提交了凑数,结果发现TLE了。这道题比上道题题难些,简单暴力搜索无法通过了。看了discuss,提到一种新的算法,不想在学个冷僻的算法了。于是想到数独是AI中的基本题目,当时学到的基本策略在这里应该也是适用的。

于是看到了AI课本的作者Peter Norvig的post:Solving Every Sudoku Puzzle(http://norvig.com/sudoku.html),用的是CSP(constraint satisfaction problem)中的基本思路。效果特别好。

我这里用的是上述文章的简化版本。只考虑以下优化:

1.每次尝试(DFS)时,从有超过一种可能性,但是可能性种类最少的格子开始。比如格子1有123469共6种可能性,而格子3只有27两种可能性,那我们从格子3入手,快速失败的好处是我们避免了尝试后面的所有可能性。

2.我们在确定一个格子的数后,删除同行,同列,同block中的格子中的这个数。同时,如果一个格子在更新可能性后只剩下一种选择,那么我们也同时确定了这个格子的数(发散地消除其conflict所有格子的可能性)。

由于这两种优化已经能AC了,第三种可能非常有效的优化在这个没有使用:

3.如果对于一个数,比如3,在某行,或某列,或某block中只剩下一种可能的位置了,那么那个位置(格子)的数也确定了(3)。我们可以依此继续发散。从作者的post来看,提高的空间还是很大的。

thestoryofsnow3074Accepted212K750MSC++4724B
/*
ID: thestor1
LANG: C++
TASK: poj3074
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;

int sudoku[81];

bool assign(int values[][10], int grid, int value);
bool search(int values[][10]);

void debugSudoku()
{
printf("[debug]sudoku:\n");
int i = 0;
while (i < 81)
{
printf("%d ", sudoku[i]);
i++;
if (i % 9 == 0)
{
printf("\n");
}
}
}

void printSudoku()
{
int i = 0;
while (i < 81)
{
printf("%d", sudoku[i]);
i++;
}
printf("\n");
}

void debugValues(int values[][10])
{
printf("[debug]values:\n");
for (int i = 0; i < 81; ++i)
{
printf("(%d, %d): [%d]", i / 9, i % 9, values[i][0]);
for (int n = 1; n <= 9; ++n)
{
if (values[i]
)
{
printf("%d ", n);
}
}
printf("\n");
}
}

void copyvalues2sudoku(int values[][10])
{
for (int i = 0; i < 81; ++i)
{
for (int n = 1; n <= 9; ++n)
{
if (values[i]
)
{
sudoku[i] = n;
break;
}
}
}
}

bool remove(int values[][10], int grid, int value)
{
// printf("[debug]remove value %d from (%d, %d)\n", value, grid / 9, grid % 9);
if (values[grid][value] == 1)
{
values[grid][value] = 0;
values[grid][0]--;
if (values[grid][0] < 1)
{
// printf("[debug]fail to remove as (%d, %d) will be empty without %d\n", grid / 9, grid % 9, value);
return false;
}
else if (values[grid][0] == 1)
{
for (int n = 1; n <= 9; ++n)
{
if (values[grid]
!= 0)
{
// printf("[debug](%d, %d) has only one value %d\n", grid / 9, grid % 9, n);
if (!assign(values, grid, n))
{
return false;
}
break;
}
}
}
return true;
}
else {
return true;
}
}

bool assign(int values[][10], int grid, int value)
{
values[grid][0] = 1;
for (int n = 1; n <= 9; ++n)
{
values[grid]
= 0;
}
values[grid][value] = 1;

int r = grid / 9, c = grid % 9;
// printf("[debug]assign %d to (%d, %d)\n", value, r, c);
// remove 'value' from grids in the same row
for (int i = 0; i < 9; ++i)
{
int g = r * 9 + i;
if (g != grid && !remove(values, g, value))
{
return false;
}
}

// remove 'value' from grids in the same column
for (int i = 0; i < 9; ++i)
{
int g = i * 9 + c;
if (g != grid && !remove(values, g, value))
{
return false;
}
}

// remove 'value' from grids in the same block
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
int g = (r / 3 * 3 + i) * 9 + (c / 3 * 3 + j);
if (g != grid && !remove(values, g, value))
{
return false;
}
}
}

return true;
}

bool search(int values[][10])
{
bool allone = true;
int mini = 0, minn = 9;
for (int i = 0; i < 81; ++i)
{
if (values[i][0] != 1)
{
allone = false;
if (values[i][0] < minn)
{
mini = i;
minn = values[i][0];
}
}
}

// if every grid has exactly one value, then we have solved the sudoku
if (allone)
{
// printf("[debug]copy values to sudoku\n");
// debugValues(values);
copyvalues2sudoku(values);
return true;
}

// else we start trying from the grid has the minimum number of values
int valuescopy[81][10];
for (int n = 1; n <= 9; ++n)
{
if (values[mini]
)
{
// printf("[debug]trying %d to (%d, %d)\n", n, mini / 9, mini % 9);
for (int i = 0; i < 81; ++i)
{
for (int n = 0; n <= 9; ++n)
{
valuescopy[i]
= values[i]
;
}
}
if (assign(valuescopy, mini, n))
{
if (search(valuescopy))
{
return true;
}
}
// we know that we cannot assign n to mini
// so we remove 'n' from 'values[mini]'
values[mini][0]--;
values[mini]
= false;
}
}

return false;
}

int main()
{
char ch;
int si = 0;
int values[81][10];
while (scanf("%c", &ch) > 0 && ch != 'e')
{
// printf("[%c]\n", ch);
if (ch == '.')
{
ch = '0';
}

if (!('0' <= ch && ch <= '9'))
{
continue;
}
// printf("ri: %d, ci: %d, ch: %c\n", ri, ci, ch);
sudoku[si] = ch - '0';
si++;
if (si == 81)
{
// debugSudoku();

for (int i = 0; i < 81; ++i)
{
if (sudoku[i] == 0)
{
values[i][0] = 9;
for (int n = 1; n <= 9; ++n)
{
values[i]
= 1;
}
}
else
{
values[i][0] = 1;
for (int n = 1; n <= 9; ++n)
{
values[i]
= 0;
}
values[i][sudoku[i]] = 1;
}
}

for (int i = 0; i < 81; ++i)
{
if (sudoku[i] != 0)
{
assign(values, i, sudoku[i]);
}
}

// debugValues(values);

search(values);

// debugSudoku();
printSudoku();

si = 0;
}

}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: