1. 程式人生 > >hdu1426Sudoku Killer解題報告---數獨(深搜 & 回溯 & 輸入格式控制)

hdu1426Sudoku Killer解題報告---數獨(深搜 & 回溯 & 輸入格式控制)

                                            Sudoku Killer

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10645    Accepted Submission(s): 3152


 

Problem Description

自從2006年3月10日至11日的首屆數獨世界錦標賽以後,數獨這項遊戲越來越受到人們的喜愛和重視。
據說,在2008北京奧運會上,會將數獨列為一個單獨的專案進行比賽,冠軍將有可能獲得的一份巨大的獎品———HDU免費七日遊外加lcy親筆簽名以及同hdu acm team合影留念的機會。
所以全球人民前仆後繼,為了獎品日夜訓練茶飯不思。當然也包括初學者linle,不過他太笨了又沒有多少耐性,只能做做最最基本的數獨題,不過他還是想得到那些獎品,你能幫幫他嗎?你只要把答案告訴他就可以,不用教他是怎麼做的。

數獨遊戲的規則是這樣的:在一個9x9的方格中,你需要把數字1-9填寫到空格當中,並且使方格的每一行和每一列中都包含1-9這九個數字。同時還要保證,空格中用粗線劃分成9個3x3的方格也同時包含1-9這九個數字。比如有這樣一個題,大家可以仔細觀察一下,在這裡面每行、每列,以及每個3x3的方格都包含1-9這九個數字。

例題:



答案:

Input

本題包含多組測試,每組之間由一個空行隔開。每組測試會給你一個 9*9 的矩陣,同一行相鄰的兩個元素用一個空格分開。其中1-9代表該位置的已經填好的數,問號(?)表示需要你填的數。

Output

對於每組測試,請輸出它的解,同一行相鄰的兩個數用一個空格分開。兩組解之間要一個空行。
對於每組測試資料保證它有且只有一個解。

這題很考驗編碼能力,兩組資料空行,輸入最好要用字串來處理輸入的空格換行,搜尋時還要注意位置搜尋不到後的回溯,單個點確定的九宮格判斷 & 行列重複判斷,搜尋從0開始到所有的'?'均確定

練習時這個題目標籤是基礎搜尋題...其實還是很有難度的

 AC Code:

#include <cstdio>
#include <cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
#include<climits>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
typedef long long ll;
static const int MAX_N = 1e6 + 5;

typedef pair<int, int> P;   //記錄座標
P vv[80];
int maps[10][10];
int len;
bool flag;
bool is_rigth(int x, int y, int z) {
    for(int i = 0; i < 9; i++){
        if(maps[i][y] == z) return false;
        if(maps[x][i] == z) return false;   //判斷行列
    }
    int fx = x / 3 * 3, fy = y / 3 * 3;         //確定所在九宮格
    for(int i = fx; i < fx + 3; i++){
        for(int j = fy; j < fy + 3; j++){
            if(maps[i][j] == z) return false;   //九宮格判斷
        }
    }
    return true;
}

void dfs(int s) {
    if(flag) return;    //剪枝
    if(s == len){
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 8; j++){
                printf("%d ", maps[i][j]);
            }
            printf("%d\n", maps[i][8]);
        }
        flag = true;
        return ;
    }
    for(int i = 1; i <= 9; i++){
        if(flag) return ;   //剪枝
        if(is_rigth(vv[s].first, vv[s].second, i)) {
            maps[vv[s].first][vv[s].second] = i;
            dfs(s + 1);
            if(flag) return;    //剪枝
        }
    }
    maps[vv[s].first][vv[s].second] = 0;    //回溯(該點選擇不合適,重新置0)
}
int main(){
    char s[2];
    while(scanf("%s", s) != EOF) {
       maps[0][0] = (s[0] == '?' ? 0 : s[0] - '0'); //第1行第1個
       for(int j = 1; j < 9; j++){
            scanf("%s", s);
            maps[0][j] = (s[0] == '?' ? 0 : s[0] - '0');    //第1行後8個
       }
       for(int i = 1; i < 9; i++){
            for(int j = 0; j < 9; j++) {
                scanf("%s", s);
                maps[i][j] = (s[0] == '?' ? 0 : s[0] - '0'); //2 - 9行
            }
       }
       len = 0;
       for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
                if(maps[i][j] == 0) {
                    vv[len].first = i, vv[len++].second = j;
                }
            }
       }
       if(flag) printf("\n");   //之前有過輸入
       flag = false;
       dfs(0);
    }
    return 0;
}