noip2009靶形數獨題解

2022-08-15 01:45:07 字數 4594 閱讀 7055

靶形數獨是乙個經典的np完全問題,沒有多項式演算法,顯然需要搜尋,遞迴回溯會優於列舉。然而此題資料範圍大,如果樸素搜尋顯然肯定tle,於是我們就需要一些優化。

1.在搜尋中,每次我們都需要查詢當前格仔的可填數字,如果用二進位制數集儲存的話,可以大大減少執行時間。對於乙個格仔(x,y),可選數字為x行、y列、所在九宮格的可選數字集合的交集,用二進位制儲存,利用位運算實現,可過75%的資料。

具體程式見下面。

這題還有乙個方法,用dlx(dancing links)。簡單的說, dancing links 主要是用雙向十字鍊錶來儲存稀疏矩陣,來達到在搜尋

中的優化。

在搜尋問題中,所需要儲存的矩陣往往隨著遞迴的加深會變得越

來越稀疏,這種情況用dancing links 

來儲存矩陣,往往可以取得非常好的

效果。dlx就是利用了雙向鍊錶的刪除和重新插入的方便快速,把矩陣不斷刪除一些行和列,回溯時再恢復,使得在遞迴回溯中得到相當快的速度。具體程式也就不實現了。這方面的典型題目就是精確覆蓋問題,很多數獨問題也可以轉化為此類問題。poj3074就可以用此方法。dlx是很通用的搜尋優化方法,有興趣的話,可以自己查詢資料,

這是一篇中文版的**。

view code

1

const d:array[1..9,1..9]of longword=((1,1,1,1,1,1,1,1,1),

2 (1,2,2,2,2,2,2,2,1),

3 (1,2,3,3,3,3,3,2,1),

4 (1,2,3,4,4,4,3,2,1),

5 (1,2,3,4,5,4,3,2,1),

6 (1,2,3,4,4,4,3,2,1),

7 (1,2,3,3,3,3,3,2,1),

8 (1,2,2,2,2,2,2,2,1),

9 (1,1,1,1,1,1,1,1,1));

10 c:array[1..9,1..9]of longword=((1,1,1,2,2,2,3,3,3),

11 (1,1,1,2,2,2,3,3,3),

12 (1,1,1,2,2,2,3,3,3),

13 (4,4,4,5,5,5,6,6,6),

14 (4,4,4,5,5,5,6,6,6),

15 (4,4,4,5,5,5,6,6,6),

16 (7,7,7,8,8,8,9,9,9),

17 (7,7,7,8,8,8,9,9,9),

18 (7,7,7,8,8,8,9,9,9));

19var i,j,n,m,k,l,min,x,y:longword;

20 a:array[0..9,0..9]of longword;

21 s1,s2,s3:array[0..9]of longword;

22 p:array[0..9,0..9]of boolean;

23 bo:boolean;

24 ans:longint;

25function getnum(k:longword):longword;

26begin

27 k:=(k and $55555555)+((k shr 1)and $55555555);

28 k:=(k and $33333333)+((k shr 2)and $33333333);

29 k:=(k and $0f0f0f0f)+((k shr 4)and $0f0f0f0f);

30 k:=(k and $00ff00ff)+((k shr 8)and $00ff00ff);

31 k:=(k and $0000ffff)+((k shr 16)and $0000ffff);

32 exit(k);

33end;

34procedure getmin(var x1,y1:longword);

35var i,j,k,min,l:longword;

36begin

37 min:=maxlongint;

38for i:=1to9

do39

for j:=1to9

do40

if (a[i,j]=0) then

41begin

42 k:=s1[i] or s2[j] or s3[c[i,j]];

43 k:=k xor 511;

44 k:=getnum(k);

45if (kthen

begin min:=k;x1:=i;y1:=j;end;

46end;

47end;

48procedure go(step:longword);

49var i,j,k,l,z,t,x,y:longword;

50 bak1,bak2,bak3:array[0..9]of longword;

51begin

52if step=m+1

then

53begin

54 t:=0;

55for i:=1to9

do56

for j:=1to9

do57 t:=t+a[i,j]*(d[i,j]+5);

58if ansthen ans:=t;

59 exit;

60end;

61 getmin(x,y);

62 k:=511 xor(s1[x] or s2[y] or s3[c[x,y]]);

63while k<>0

do64

begin

65 l:=k and -k;

66 k:=k-l;

67 bak1:=s1;bak2:=s2;bak3:=s3;

68 s1[x]:=s1[x] or l;

69 s2[y]:=s2[y] or l;

70 s3[c[x,y]]:=s3[c[x,y]] or l;

71case l of

721:j:=1;

732:j:=2;

744:j:=3;

758:j:=4;

7616:j:=5;

7732:j:=6;

7864:j:=7;

79128:j:=8;

80256:j:=9;

81end;

82 a[x,y]:=j;

83 go(step+1);

84 a[x,y]:=0;

85 s1:=bak1;s2:=bak2;s3:=bak3;

86end;

87end;

88begin

89 assign(input,'

sudoku.in

');reset(input);

90 assign(output,'

sudoku.out

');rewrite(output);

91 fillchar(a,sizeof(a),0);

92 m:=81;

93for i:=1to9

do94

for j:=1to9

do95

begin

96 read(a[i,j]);

97if a[i,j]<>0

then

98begin

99 s1[i]:=s1[i] or(1 shl(a[i,j]-1));

100 s2[j]:=s2[j] or(1 shl(a[i,j]-1));

101 s3[c[i,j]]:=s3[c[i,j]] or(1 shl(a[i,j]-1));

102 dec(m);

103end;

104end;

105 ans:=-1;

106 go(1);

107 writeln(ans);

108 close(input);close(output);

109end.

NOIP2009 靶形數獨 題解

題目名稱 靶形數獨 題目 noip2009 時間限制 2000ms 空間限制 128m 問題描述 小城和小華都是熱愛數學的好學生,最近,他們不約而同地迷上了數獨遊戲,好勝的他們想用數獨來一比高低。但普通的數獨對他們來說都過於簡單了,於是他們向z博士請教,z博士拿出了他最近發明的 靶形數獨 作為這兩個...

NOIP 2009 靶形數獨題解

題目的一些資訊就不給了,點鏈結吧。解題思路 我總結為乙個要點,乙個優化 要點 搜尋順序不能 for i 1 i 9 i for j 1 j 9 j 這樣時間複雜度過不去。如何搜尋?當做填數字一樣,每乙個格仔列舉陣列,從上到下,從左到右填過去。優化 這真的是欺負我這種沒有玩過數獨的人。從已經填數字多的...

NOIP2009 靶形數獨

爆搜沒什麼好說的。剪枝思路 一開始將每個點可能取的值的數量統計出,排序,從小到大搜 然後貪心可行性 就是剩下的地方都填9,得分10 不過在vj上測85。日。加了卡時,2e7次之內跳出,總算過了。include include include include include include inclu...