本週的AI引論做業佈置了一道數獨ios
加了奇怪剪枝仍然TLE的Candy?不得不去學了dlx數組
dlxnb!數據結構
設全集X,X的若干子集的集合爲S。精確覆蓋是指,選擇一個S的子集S‘,知足X中的每個元素在S’中剛好出現一次。優化
是一個NPC問題。spa
能夠表示成01矩陣形式,選擇若干行,使得每一列剛好有且僅有一行爲1.指針
數獨能夠轉化爲精確覆蓋問題。code
令N=81爲數獨中格子個數,則:blog
(x, y)=1
表示(x,y)
處填了數(x+N, z)=1
表示x行填了z(y+N*2, z)=1
表示y列填了z(r+N*3, z)=1
表示r宮填了z對於已經填了數的格子,轉化爲1行;遞歸
對於空的格子,轉化爲9行。ci
一種顯然的dfs:
一個顯然的啓發式優化:minimum-remaining-values(MRV) heuristic
Dancing links is the technique suggested by Donald Knuth to efficiently implement his Algorithm X.
是一種用來高效實現algorithm X的數據結構。
就是「交叉十字循環雙向鏈表」。
第0行分別是root和每一列的列首節點
其餘的只有爲1的位置纔有節點。
刪除某一列時,只要處理該列首節點(包括其左右節點)的左右指針;
刪除某列時同時要刪除該列上爲1的全部行;
刪除某一行時,只要處理該行全部節點(包括其上下節點)的上下指針。
值得注意的是,刪除以後該列/行的結構沒有改變。
每一個節點維護:
l r u d
左右上指針col
列指針row
行標號cnt
保存該列的元素個數(只列首/用來MRV優化)a
和h
數組保存列首/行首節點指針
init
(r,c)
位置加入一個元素/1 link
a[c]
下,h[r]
右del
add
dance
root->r == root
時完成
選擇元素最少的某列c
並刪除該列(包括該列上爲1的全部行)
選擇該列上爲1的某行,刪除該行(包括該行上爲1的全部列)
實際上這一行在2中已經刪除了,只要處理該行的列便可
遞歸搜索
恢復該行
恢復該列
==注意==
POJ 3076 16*16數獨問題的代碼
結構體版太醜了仍是放指針版吧QwQ
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <ctime> using namespace std; const int NUM = 260*4*16, N = 260*16, K = 16, L = 4, M = N*16; int n = 256*4, num = 256, m=0; struct meow { meow *l, *r, *u, *d, *col; int row; int cnt; } pool[NUM]; meow *a[NUM], *h[M], *root; int ans[N], sz; char s[20][20]; struct action { int x, y, z; } q[M]; void init() { for(int i=0; i<=n; i++) a[i] = &pool[i]; for(int i=0; i<=n; i++) { a[i]->l = a[i-1]; a[i]->r = a[i+1]; a[i]->u = a[i]->d = a[i]; a[i]->col = a[i]; a[i]->row = 0; a[i]->cnt = 0; } a[0]->l = a[n]; a[n]->r = a[0]; root = a[0]; sz = n; memset(h, 0, sizeof(h)); } void link(int r, int c) { sz++; meow *x = a[sz] = &pool[sz]; x->row = r; x->col = a[c]; a[c]->cnt++; x->d = a[c]->d; x->d->u = x; x->u = a[c]; x->u->d = x; if(h[r] == NULL) { h[r] = x->l = x->r = x; } else { x->r = h[r]->r; x->r->l = x; x->l = h[r]; x->l->r = x; } } void del(meow *x) { x->l->r = x->r; x->r->l = x->l; for(meow *i = x->d; i != x; i = i->d) for(meow *j = i->r; j != i; j = j->r) { j->d->u = j->u; j->u->d = j->d; j->col->cnt--; } } void add(meow *x) { x->l->r = x->r->l = x; for(meow *i = x->u; i != x; i = i->u) for(meow *j = i->l; j != i; j = j->l) { j->u->d = j->d->u = j; j->col->cnt++; } } bool dance(int k) { if(root->r == root) { for(int i=1; i<=num; i++) { action &x = q[ans[i]]; s[x.x][x.y] = 'A' + x.z-1; } return true; } meow *c = root; c->cnt = 1e9; for(meow *x = root->r; x != root; x = x->r) if(x->cnt < c->cnt) c = x; del(c); for(meow *i = c->d; i != c; i = i->d) { ans[k+1] = i->row; for(meow *j = i->r; j != i; j = j->r) del(j->col); if(dance(k+1)) return true; for(meow *j = i->l; j != i; j = j->l) add(j->col); } add(c); return false; } inline int grid_id(int x, int y, int k=L) {return (x-1)/k*k + (y-1)/k+1;} void sudoku(int x, int y, int z) { m++; link(m, (x-1)*K+y); link(m, (x-1)*K+z + num); link(m, (y-1)*K+z + num*2); link(m, (grid_id(x, y)-1)*K+z + num*3); q[m] = (action) {x, y, z}; } int main() { while(scanf("%s", s[1]+1) != EOF) { init(); for(int i=1; i<=K; i++) { for(int j=1; j<=K; j++) { int a; if(s[i][j] == '-') a = 0; else a = s[i][j]-'A'+1; if(a != 0) sudoku(i, j, a); else for(int k=1; k<=K; k++) sudoku(i, j, k); } if(i != K) scanf("%s", s[i+1]+1); } dance(0); for(int i=1; i<=K; i++) { for(int j=1; j<=K; j++) printf("%c", s[i][j]); puts(""); } puts(""); } }