1317. Sudoku

解题技巧:1.数独每个格子记录自身还能填的数字,每次寻找能填数字数最少的格子填充数字。

     2.使用“禁用计数”的方式,记录每个格子某个数字被禁用的次数,以便脱离禁用后恢复可填性。

#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
using std::set;
using std::vector;

struct Elem {
    int num;
    set<int> lefts;
};

set<int> LEFTS;

const int maxn = 9;
char charMap[maxn][maxn+1];
Elem Map[maxn][maxn];

int ans[maxn][maxn];
int count;
int f[maxn][maxn][maxn + 1];


struct Cor {
    int r, c;
    Cor(int r_ = 0, int c_ = 0) : r(r_), c(c_) {}
    bool operator<(const Cor &cor) const {
        return (r < cor.r) || (r == cor.r && c < cor.c);
    }
};

set<Cor> unfills;


void init() {
    for (int i = 0; i < maxn; ++i) {
        for (int j = 0; j < maxn; ++j) {
            Map[i][j].num = 0;
            Map[i][j].lefts.insert(LEFTS.begin(), LEFTS.end());
        }
    }
    for (int i = 0; i < maxn; ++i) {
        for (int j = 0; j < maxn; ++j) {
            for (int k = 0; k < maxn; ++k) {
                f[i][j][k + 1] = 0;
            }
        }
    }
    count = 0;
    unfills.clear();
}

void input() {
    for (int i = 0; i < maxn; ++i) {
        scanf("%s", charMap[i]);
    }
}

void transform() {
    for (int i = 0; i < maxn; ++i) {
        for (int j = 0; charMap[i][j] != '\0'; ++j) {
            if (charMap[i][j] != '_') {
                Map[i][j].num = charMap[i][j] - '0';
            } else {
                unfills.insert(Cor(i, j));
            }
        }
    }
}

void setting(int r, int c, int num) {
    Map[r][c].num = num;
    for (int i = 0; i < maxn; ++i) {
        Map[r][i].lefts.erase(num);
        ++f[r][i][num];
        Map[i][c].lefts.erase(num);
        ++f[i][c][num];
    }
    int beginr = r / 3 * 3;
    int beginc = c / 3 * 3;
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            Map[i + beginr][j + beginc].lefts.erase(num);
            ++f[i + beginr][j + beginc][num];
        }
    }
}

void resume(int r, int c, int num) {
    Map[r][c].num = 0;
    for (int i = 0; i < maxn; ++i) {
        if (--f[r][i][num] == 0) Map[r][i].lefts.insert(num);
        if (--f[i][c][num] == 0) Map[i][c].lefts.insert(num);
    }
    int beginr = r / 3 * 3;
    int beginc = c / 3 * 3;
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            if (--f[i+beginr][j+beginc][num] == 0) Map[i + beginr][j + beginc].lefts.insert(num);
        }
    }
}

void predeal() {
    for (int i = 0; i < maxn; ++i) {
        for (int j = 0; j < maxn; ++j) {
            if (Map[i][j].num != 0) {
                setting(i, j, Map[i][j].num);
            }
        }
    }
}

void dfs() {
    if (unfills.empty()) {
        // 达到一种目的状态
        ++count;
        if (count > 1) return;
        for (int i = 0; i < maxn; ++i) {
            for (int j = 0; j < maxn; ++j) {
                ans[i][j] = Map[i][j].num;
            }
        }
    } else {
        // 遍历unfills,寻找最小的
        set<Cor>::iterator iter = unfills.begin(), target = unfills.begin();
        while (iter != unfills.end()) {
            const Cor &cor1 = *iter;
            const Cor &cor2 = *target;
            if (Map[cor1.r][cor1.c].lefts.size() < Map[cor2.r][cor2.c].lefts.size()) {
                target = iter;
            }
            ++iter;
        }
        Cor cor = *target;
        unfills.erase(target);
        set<int> _lefts(Map[cor.r][cor.c].lefts);   // 
        set<int>::iterator iter1 = _lefts.begin();
        while (iter1 != _lefts.end()) {
            int num = *iter1;
            setting(cor.r, cor.c, num);
            dfs();
            resume(cor.r, cor.c, num);
            ++iter1;
        }
        unfills.insert(cor);
    }
}

int main() {
    for (int i = 1; i <= 9; ++i) LEFTS.insert(i);
    int t;
    scanf("%d", &t);
    for (int tt = 1; tt <= t; ++tt) {
        // 初始化
        init();
        // 输入
        input();
        // 转化
        transform();
        // 预处理
        predeal();
        // 深搜
        dfs();

        if (count == 0) {
            printf("Puzzle %d has no solution\n", tt);
        } else if (count == 1) {
            printf("Puzzle %d solution is\n", tt);
            for (int i = 0; i < maxn; ++i) {
                for (int j = 0; j < maxn; ++j) {
                    printf("%d", ans[i][j]);
                }
                printf("\n");
            }
        } else if (count > 1) {
            printf("Puzzle %d has %d solutions\n", tt, count);
        }
        if (tt != t) printf("\n");
    }

    return 0;
}

  

posted @ 2016-01-06 11:32  MchCyLh  阅读(330)  评论(0编辑  收藏  举报