如圖所示,這是一個8*5數碼的問題,當然,用之前的范式就不好辦了。但是,之前也有說明十五數碼的唯一解定理,由此,我們可以提出N數碼問題的唯一解。
N數碼問題的唯一解
逆序對?空白(blank)起始時所在的點的行距距離目標點的歐幾里得距離(dis)?這是在N*N-1數碼問題中當N為偶數的時候能成立,當N為奇數的 時候,條件需要更弱一些,也就是說,當N為奇數的時候,在逆序和保持奇偶性相同的情況下,就能保證問題是唯一可解的。基於這一點(定理的證明可以詳見相關 資料,或者按照15數碼解的存在性定理的證明方法給出證明),這里給出判定N數碼問題是否有唯一解的判定程序(HDOJ 3600):
2
3 #include<algorithm>
4
5 // 利用string庫來讀入一個謎題
6
7 #include< string>
8
9 using namespace std;
10
11
12
13 // 假設問題的規模(N的最大值不超過300)
14
15 const int N= 300+ 10;
16
17
18
19 int s[N*N],g[N*N];
20
21
22
23 // 利用宏定義一個函數
24
25 #define _cp(a,b) ((a)<=(b))
26
27
28
29 int _tmp[N*N];
30
31
32
33 // 考察這個問題對應的所有狀態的逆序數之和
34
35 int solve( int n, int *a)
36
37 {
38
39 int l=n>> 1;
40
41 int i,j,r=n- 1;
42
43 // 如下是求兩個狀態逆序對的差值,不過,這段代碼確實有些亂,暫時沒有弄明白
44
45 int ret=(r> 1 ? (solve(l,a)+solve(r,a+l)): 0);
46
47 for(i=j= 0;i<=l;_tmp[i+j]=a[i],i++)
48
49 {
50
51 for(ret+=j;j<r&&(i==l||!_cp(a[i],a[l+j]));_tmp[i+j]=a[l+j],j++);
52
53 }
54
55 memcpy(a,_tmp, sizeof( int)*n);
56
57 return ret;
58
59 }
60
61
62
63 int main()
64
65 {
66
67 int n;
68
69 // 每次讀入一個問題前,先讀入問題的規模
70
71 while(scanf( " %d ",&n)== 1&&n)
72
73 {
74
75 int num= 0;
76
77 bool flag= true;
78
79 // 讀入整個問題
80
81 for( int i= 0;i<n*n;i++)
82
83 {
84
85 scanf( " %d ",&g[i]);
86
87 // 這里可以找到那個空白方塊的位置
88
89 if(flag&&g[i]!= 0) num++;
90
91 if(g[i]== 0) flag= false;
92
93 }
94
95 int temp=solve(n*n,g);
96
97 // 不考慮空白位置的逆序數
98
99 temp-=num;
100
101 // 如果n為偶數的話,需要加上空格所在的行距離目標空格的行的dis距離
102
103 if(!(n& 1))
104
105 {
106
107 temp+=(n- 1-(num/n));
108
109 }
110
111 // 如果奇偶相異,則不可達,否則,為可達
112
113 if(temp& 1) puts( " NO ");
114
115 else puts( " YES ");
116
117 }
118
119 return 0;
120
121 }
N*M問題的唯一解
在POJ的2893中,原問題被更進一步地擴展,但是,原理還是相同的,而且,我們可以得到更為本質化的結論:當N為完全平方模型的時候,似乎是由N來決 定原理的,而實際上,如果問題被延拓為N*M,我們可以發現,一個更為本質的結論,原問題的可解性實際上是由列數M來判定的,這樣,由特殊推廣到了一般, 證明如下(這里我們可以看出,行和列都起到了一定的作用,列的作用確定了空白方塊是否要考慮,而在空白方塊的位置需要考慮的情況下,行的作用又讓它必須了解到底要增加多少(也就是行增量,歐幾里得距離)):
假設這個矩陣有3列,且逆序對總數設為N(這里不失一般性地假設a1<a2)
(1)首先,對於可左右移動的數字,它左右移動是不會對逆序對總數產生影響;
(2)若數字a0上下移動的話會移到它之前或之后的兩個數字的位置,假設是向上移動,且這兩個數字為a1,a2:(3個數字肯定是不同數字)
若a0 < a1 < a2,則新的逆序對總數為N-2,
若a1 < a0 < a2, 則新的逆序對總數為N - 1 + 1 = N,
若a1 < a2 < a0, 則新的逆序對總數為N+2,
由此可見,數字的移動是不影響矩陣逆序對總數的奇偶性的,推廣到列數為奇數的情況也是一樣。那么若判定初始矩陣與目標矩陣的逆序對總數是否相同,即可判定問題是否可解,若同則可解,否則不可解。
若列數為偶數的話,數字左右移動同理不影響,但是上下移動一次會改變一次逆序對總數的奇偶性,所以對於這種情況,我們可以計算初始矩陣0的位置到目標矩陣 0的位置的差值 S ,這樣可以判定奇偶性被如何改變了。由於0最終是要到目標位置的,所以無論0在工程中如何移動,逆序對的奇偶性只和S有關。那有些人可能會想,我們怎么只 考慮0的上下移動,不考慮其他數字的上下移動,我們可以這樣想,無論那個數字要移動都是要和0交換位置,也就是說無論那個數字的移動都是0的移動,所以在 這里我們就只分析0的上下移動即可。
這里可以采用歸並排序來解決逆序對的問題,代碼如下(這里換一種方法,利用歸並排序來求逆序對的總數之和):
2
3
4
5 using namespace std;
6
7
8
9 #define MAXN 1000005
10
11
12
13 int cnt;
14
15 int a[MAXN],c[MAXN];
16
17
18
19 // 利用歸並排序來求逆序數
20
21 void MergeSort( int l, int r)
22
23 {
24
25 int mid,i,j,tmp;
26
27 // 直到左右縫合為止
28
29 if(r>l+ 1)
30
31 {
32
33 mid=(l+r)/ 2;
34
35 MergeSort(l,mid);
36
37 MergeSort(mid,r);
38
39 tmp=l;
40
41 // 找到所有的逆序對,這一段也暫時不是很明白
42
43 for(i=l,j=mid;i<mid&&j<r;)
44
45 {
46
47 if(a[i]>a[j])
48
49 {
50
51 c[tmp++]=a[j++];
52
53 cnt+=mid-i;
54
55 }
56
57 else c[tmp++]=a[i++];
58
59 }
60
61 while(j<r) c[tmp++]=a[j++];
62
63 while(i<mid) c[tmp++]=a[i++];
64
65 for(i=l;i<r;++i) a[i]=c[i];
66
67 }
68
69 }
70
71
72
73 int main()
74
75 {
76
77 int n,m,pl;
78
79 // 讀入問題的規模,以(0,0)結尾
80
81 while(scanf( " %d%d ",&n,&m)!=EOF)
82
83 {
84
85 if(n == 0 || m == 0) break;
86
87 // 這個用來標記逆序對的和
88
89 cnt = 0;
90
91 // 這個用來標明當前處理的方塊的位置
92
93 pl = 0;
94
95 int x,loc0;
96
97 for( int i = 0; i < n*m; i++)
98
99 {
100
101 scanf( " %d ",&a[pl]);
102
103 if(a[pl] == 0)
104
105 {
106
107 x = pl/m;
108
109 loc0 = pl;
110
111 }
112
113 pl++;
114
115 }
116
117 MergeSort( 0,pl);
118
119 // 扣除0的逆序數
120
121 cnt -= loc0;
122
123 int step = 0;
124
125 // 若列數為奇數,則數字的上下左右移動都不影響最終逆序對的總數
126
127 if(m% 2 == 1)
128
129 step = 0;
130
131 // 若列數為偶數,左右移動不影響,但上下移動一次,會改變一次逆序對總數的奇偶性
132
133 else
134
135 step = n - 1 - x;
136
137 // 初始,結束狀態的逆序對總數奇偶性一致,則問題可解
138
139 if(step% 2 == cnt % 2)
140
141 printf( " YES\n ");
142
143 else
144
145 printf( " NO\n ");
146
147 }
148
149 return 0;
150
151 }
進一步推廣,魔方,七巧板,華容道……
如圖,這是一個魔方,是由N*N*N個小立方體組成的。我們這里隨意拿走一個小立方體,並假設整個結構沒有被破壞。那么,這是一次更普遍的飛躍,由二維空間上升到了三維空間。但是,其解的唯一性定律還適用嘛?答案是肯定的,證明如下:
魔方數碼問題的解的唯一性定理
考慮左右移動空格,逆序不變;同一層上下移動空格,跨過N-1個格子;上下層移動空格,跨過N^2-1個格子。
當N為奇數時,N-1和N^2-1均為偶數,也就是任意移動空格逆序奇偶性不變。那么逆序奇偶性相同的兩個狀態可相互到達。
當N為偶數時,N-1和N^2-1均為奇數,也就是令空格位置到目標狀態空格位置的y z方向的距離之和,稱為空格距離。若空格距離為偶數,兩個逆序奇偶性相同的狀態可相互到達;若空格距離為奇數,兩個逆序奇偶性不同的狀態可相互到達。
七巧板
更為一般性的拓展,我們可以考慮到其圖形不一定是正方形或者是正方體,比如,我們國家民間的七巧板游戲,這些方塊的形狀都不是正方形,甚至彼此都不一定相 同,但是,經過一定的組合之后,卻構成了一個比較好玩的游戲。但是,這個游戲並不是通過方塊的滑動來實現的,而是放置,所以,在這一點上看,軟件的實現比 較簡單。
其實現並不復雜,只要給出固定的解,每一次比對是否將圖形安裝到固定的位置就可以了,如圖所示,這是其中的一款游戲:
華容道
更復雜的游戲,比如華容道的AI,就要考慮很多因素,因為,不同的方塊形狀是不一樣的,不過,索性還有一定的規則可以追尋,其實現目前很多,我用的是一個中學老師的AI版本(他寫成了一篇論文)。我之后會單獨以一個Round來放送的。
---恢復內容結束---