1. 程式人生 > 實用技巧 >「題解」Solution P4573

「題解」Solution P4573

做這道題的原因:上課教練給了一道這題的加強版,然後我就走入了一條不歸路。

本題連結 加強版連結

Description

給定一個 \(9 \times 9\) 的數獨,並且每一個格子都與與他在同一個宮格里的數有大小關係,求填數獨。

Solution 1

最簡單的做法,直接 dfs。

先搞一個生成數獨 dfs 出來,然後套進去輸入。

輸入如果嫌麻煩的話可以試試將所有行和列的大小關係存入陣列,找一找規律不難發現,無論是上下還是左右關係,都是當編號不為 \(3\) 的倍數是才會有關係,並且先左右後上下,因此我們可以這麼輸入:

for (int i = 1; i <= 9; i++) {
	for (int j = 1; j <= 9; j++) { // left and right
		if (j % 3 == 0) continue;
			scanf(" %c", &lvr[i][j]);
	}
	for (int j = 1; j <= 9; j++) { // up and down 
		if (i % 3 == 0) continue;
			scanf(" %c", &uvd[i][j]);
	}
}

然後在普通 dfs 生成數獨的特判中套進去關係特判即可:

if (x > 1 && x - 1 % 3 != 0) {
	char tmp = uvd[x - 1][y];
	if (tmp == '^') 
		if (a[x - 1][y] > i) 
			continue;
	if (tmp == 'v')
		if (a[x - 1][y] < i)
			continue;
} // a[x - 1][y] & a[x][y]
if (y > 1 && y - 1 % 3 != 0) {
	char tmp = lvr[x][y - 1];
	if (tmp == '<')
		if (a[x][y - 1] > i)
				continue;
	if (tmp == '>')
		if (a[x][y - 1] < i)
			continue;
} // a[x][y - 1] & a[x][y]

dfs 生成數獨的程式碼不放了,可以看看 這題

然後用這個思路可以過本題,但是教練的加強版並過不了,只能獲得 \(40\) 分的好成績(

Solution 2

上面的程式碼我們是先按照行,再按照列的順序進行列舉,一個格子只能最多判斷兩個關係,太浪費了。

因此我們可以先列舉宮格,再列舉行,再列舉列。

這塊我們不能再採用二維 dfs 了,因為我們的思路是基於一個結構體排序。

建立一個結構體,編號為 \(cur\) 的點記錄他的行,列和宮格。

按照排序的思路,也可以把排序函式寫出來:

struct qaq {
	int Col;
	int Row;
	int Gongge;
} sudaku[90];

bool fjrakioi (qaq x, qaq y) {
	if (x.Gongge != y.Gongge) return x.Gongge < y.Gongge;
	else if (x.Col != y.Col) return x.Col < y.Col;
	else return x.Row < y.Row; 
} // fjr AK IOI !!!!!!!!!!!!!

需要修改的部分只有 dfs 中的特判邊界條件(從 \(x>9\) 變為 \(cur>81\))還有 dfs 內的當前局面變數(\(x,y\) 變為 \(cur\),為了方便可以定義另定義 \(x,y\)x = sudaku[cur].Col, y = sudaku[cur].Row),其他貌似並沒有什麼不一樣,這個做法可以在加強版中獲得 \(55\) 分的好成績(

Solution 3

這道題是在我們學拓撲排序的時候給的,所以這題可以用上拓撲排序(

因為這題有不同格子之間的大小關係,所以我們可以將大小關係化為拓撲排序中的圖的邊(這個對於拓撲排序題來說挺套路的吧 qwq),然後在結構體中加上一個變數 \(tp\) 為這個格子在拓撲排序中的位置,然後按照宮格,\(tp\),行,列的順序進行排序:

struct qaq {
	int Col;
	int Row;
	int Gongge;
	int Tuopu;
} sudaku[90];

bool fjrakioi (qaq x, qaq y) {
	if (x.Gongge != y.Gongge) return x.Gongge < y.Gongge;
	else if (x.Tuopu != y.Tuopu) return x.Tuopu < y.Tuopu;
	else if (x.Col != y.Col) return x.Col < y.Col;
	else return x.Row < y.Row; 
}

轉化圖的時候有一點比較麻煩,就是圖的點編號是一個一維的,但是數獨矩陣是二維的,怎麼轉化呢?

教練把點轉化為了二維,我覺得不必要,可以把格子的二維轉化為一維,公式是 \((x-1) \times 9+y\),注意一下邊界即可。

然後在 dfs 數獨的特判中要從四個特判改成八個特判,因為你不知道你四周哪個點比你的拓撲排序位置靠前,所以乾脆都特判一遍:

if (x > 1 && x - 1 % 3 != 0 && a[x - 1][y] != 0) {
	char tmp = uvd[x - 1][y];
		if (tmp == '^') 
			if (a[x - 1][y] > i) 
				continue;
		if (tmp == 'v')
			if (a[x - 1][y] < i)
				continue;
} // a[x - 1][y] & a[x][y]
if (y > 1 && y - 1 % 3 != 0 && a[x][y - 1] != 0) {
	char tmp = lvr[x][y - 1];
		if (tmp == '<')
			if (a[x][y - 1] > i)
				continue;
		if (tmp == '>')
			if (a[x][y - 1] < i)
				continue;
} // a[x][y - 1] & a[x][y]
if (x < 9 && x + 1 % 3 != 0 && a[x + 1][y] != 0) {
	char tmp = uvd[x + 1][y];
		if (tmp == '^') 
			if (a[x + 1][y] > i) 
				continue;
		if (tmp == 'v')
			if (a[x + 1][y] < i)
				continue;
} // a[x + 1][y] & a[x][y]
if (y < 9 && y + 1 % 3 != 0 && a[x][y + 1] != 0) {
	char tmp = lvr[x][y + 1];
		if (tmp == '<')
			if (a[x][y + 1] > i)
				continue;
		if (tmp == '>')
			if (a[x][y + 1] < i)
				continue;
} // a[x][y + 1] & a[x][y]

但是,這個程式碼仍然是 \(55\),書蟲一怒之下,求助了教練(

教練說可以加一步特判,即比如說這個格子 \(cur_1\) 比他上面的格子 \(cur_2\) 要大,那麼如果確定了 \(cur_2\),那麼就不需要在 \([1,9]\) 的區間裡列舉 \(cur_1\),在 \([cur_2,9]\) 的區間裡列舉 \(cur_1\) 即可,因此列舉 for 迴圈可以改一步:

int Max = -1;
if (x != 1 && a[x - 1][y] != 0 && uvd[x - 1][y] == '^') Max = max(Max, a[x - 1][y]);
if (x != 9 && a[x + 1][y] != 0 && uvd[x + 1][y] == 'v') Max = max(Max, a[x + 1][y]);
if (y != 1 && a[x][y - 1] != 0 && lvr[x][y - 1] == '<') Max = max(Max, a[x][y - 1]);
if (y != 9 && a[x][y + 1] != 0 && lvr[x][y + 1] == '>') Max = max(Max, a[x][y + 1]);
if (Max == -1) Max = 1;
for (int i = Max; i <= 9; i++)

然後,還是沒卡過 \(55\)

然後教練把時限開到了 \(5\)
然後全機房的同學爽了一把,AC 了
然後我居然跑了個機房最優解(5.5s)smg
然後 fjr \(5\) 秒的都沒過 smg

Code

說了這麼多,放一個教練加強版最慢點跑 \(3\) 秒多的程式碼吧:

#include <bits/stdc++.h>

using namespace std;

int zqzakioi (int x, int y) {
    return (x - 1) * 9 + y;
} 

int head[100]; 
int cnt;
int indeg[100];
int tuopu[100]; // index of tuopu
int Index;

struct node {
    int u, v;
} e[100];

void AddEdge (int u, int v) {
    e[++cnt].u = v;
    e[cnt].v = head[u];
    head[u] = cnt;
}

int Box[11][11] = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 1, 1, 1, 2, 2, 2, 3, 3, 3}, 
    {0, 1, 1, 1, 2, 2, 2, 3, 3, 3},
    {0, 1, 1, 1, 2, 2, 2, 3, 3, 3},
    {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
    {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
    {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
    {0, 7, 7, 7, 8, 8, 8, 9, 9, 9},
    {0, 7, 7, 7, 8, 8, 8, 9, 9, 9},
    {0, 7, 7, 7, 8, 8, 8, 9, 9, 9}
};

struct qaq {
    int Col;
    int Row;
    int Gongge;
    int Tuopu;
} sudaku[90];

bool fjrakioi (qaq x, qaq y) {
    if (x.Gongge != y.Gongge) return x.Gongge < y.Gongge;
    else if (x.Tuopu != y.Tuopu) return x.Tuopu < y.Tuopu;
    else if (x.Col != y.Col) return x.Col < y.Col;
    else return x.Row < y.Row; 
}

bool col[11][11]; // hang
bool row[11][11]; // lie
bool box[11][11]; // gongge

int a[11][11];

bool lvr[11][11]; // left and right
bool uvd[11][11]; // up and down

void dfs (int cur) {
    if (cur > 81) {
        for (int i = 1; i <= 9; i++)  {
            for (int j = 1; j <= 9; j++)
                printf("%d ", a[i][j]);
            puts("");
        } 
        exit(0);
    }
    int x = sudaku[cur].Col;
    int y = sudaku[cur].Row;
    int Max = -1;
    if (x != 1 && a[x - 1][y] != 0 && uvd[x - 1][y] == '^') Max = max(Max, a[x - 1][y]);
    if (x != 9 && a[x + 1][y] != 0 && uvd[x + 1][y] == 'v') Max = max(Max, a[x + 1][y]);
    if (y != 1 && a[x][y - 1] != 0 && lvr[x][y - 1] == '<') Max = max(Max, a[x][y - 1]);
    if (y != 9 && a[x][y + 1] != 0 && lvr[x][y + 1] == '>') Max = max(Max, a[x][y + 1]);
    if (Max == -1) Max = 1;
    for (int i = Max; i <= 9; i++) {
        if (col[x][i] == true) continue;
        if (row[y][i] == true) continue;
        if (box[Box[x][y]][i] == true) continue;
        if (x > 1 && x - 1 % 3 != 0 && a[x - 1][y] != 0) {
            char tmp = uvd[x - 1][y];
            if (tmp == '^') 
                if (a[x - 1][y] > i) 
                    continue;
            if (tmp == 'v')
                if (a[x - 1][y] < i)
                    continue;
        } // a[x - 1][y] & a[x][y]
        if (y > 1 && y - 1 % 3 != 0 && a[x][y - 1] != 0) {
            char tmp = lvr[x][y - 1];
            if (tmp == '<')
                if (a[x][y - 1] > i)
                    continue;
            if (tmp == '>')
                if (a[x][y - 1] < i)
                    continue;
        } // a[x][y - 1] & a[x][y]
        if (x < 9 && x + 1 % 3 != 0 && a[x + 1][y] != 0) {
            char tmp = uvd[x + 1][y];
            if (tmp == '^') 
                if (a[x + 1][y] > i) 
                    continue;
            if (tmp == 'v')
                if (a[x + 1][y] < i)
                    continue;
        } // a[x + 1][y] & a[x][y]
        if (y < 9 && y + 1 % 3 != 0 && a[x][y + 1] != 0) {
            char tmp = lvr[x][y + 1];
            if (tmp == '<')
                if (a[x][y + 1] > i)
                    continue;
            if (tmp == '>')
                if (a[x][y + 1] < i)
                    continue;
        } // a[x][y + 1] & a[x][y]
        a[x][y] = i;
        col[x][i] = true;
        row[y][i] = true;
        box[Box[x][y]][i] = true;
        dfs(cur + 1);
        col[x][i] = false;
        row[y][i] = false;
        box[Box[x][y]][i] = false;
        a[x][y] = 0;
    }
}

int main () {
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= 9; j++) { // left and right
            if (j % 3 == 0) continue;
            scanf(" %c", &lvr[i][j]);
        }
        for (int j = 1; j <= 9; j++) { // up and down 
            if (i % 3 == 0) continue;
            scanf(" %c", &uvd[i][j]);
        }
    }
    for (int i = 1; i <= 9; i++)
        for (int j = 1; j <= 9; j++) {
            int tmp = zqzakioi(i, j);
            if (j % 3 != 0 && lvr[i][j] == '<') AddEdge(tmp, tmp + 1), indeg[tmp + 1]++;
            if (j % 3 != 0 && lvr[i][j] == '>') AddEdge(tmp + 1, tmp), indeg[tmp]++;
            if (i % 3 != 0 && uvd[i][j] == '^') AddEdge(tmp, tmp + 9), indeg[tmp + 9]++;
            if (i % 3 != 0 && uvd[i][j] == 'v') AddEdge(tmp + 9, tmp), indeg[tmp]++;
        }
    queue <int> q;
    for (int i = 1; i <= 81; i++)
        if (indeg[i] == 0)
            q.push(i);
    while (!q.empty()) {
        int cur = q.front();
        tuopu[++Index] = cur;
        q.pop();
        for (int p = head[cur]; p; p = e[p].v) {
            int tmp = e[p].u;
            indeg[tmp]--;
            if (indeg[tmp] == 0) q.push(tmp);
        }
    }
    int cnt_c = 1, cnt_r = 0; 
    for (int i = 1; i <= 81; i++) {
        cnt_r++;
        if (cnt_r > 9) cnt_c++, cnt_r = 1;
        sudaku[i].Col = cnt_c;
        sudaku[i].Row = cnt_r;
        sudaku[i].Gongge = Box[cnt_c][cnt_r];
        int cnt_t;
        for (int j = 1; j <= 81; j++) 
            if (tuopu[j] == i) {
                cnt_t = i;
                break;
            }
        sudaku[i].Tuopu = cnt_t;
    }
    sort(sudaku + 1, sudaku + 82, fjrakioi);
    dfs(1);
    return 0;
}

感謝您的閱讀,希望對您有幫助