吳昊品游戲核心算法 Round 17 —— M*N PUZZLE 與 N PUZZLE 的解的唯一性定理(由特殊到一般)


  在前面,有說用各種搜索方法(后面還將給出網友整理的八數碼問題的十重境界) 來解決8 PUZZLE和15 PUZZLE問題。實際上,由於拼圖游戲的種類繁多,我們可以延拓到兩種比較特殊的類型:(1)M*N PUZZLE(2)N PUZZLE,(1)中的M*N當然是任意的正整數,而(2)中的N,這里指代的是一個正整數的平方和減一所得到的值。

 

  如圖所示,這是一個8*5數碼的問題,當然,用之前的范式就不好辦了。但是,之前也有說明十五數碼的唯一解定理,由此,我們可以提出N數碼問題的唯一解。

  N數碼問題的唯一解

  逆序對?空白(blank)起始時所在的點的行距距離目標點的歐幾里得距離(dis)?這是在N*N-1數碼問題中當N為偶數的時候能成立,當N為奇數的 時候,條件需要更弱一些,也就是說,當N為奇數的時候,在逆序和保持奇偶性相同的情況下,就能保證問題是唯一可解的。基於這一點(定理的證明可以詳見相關 資料,或者按照15數碼解的存在性定理的證明方法給出證明),這里給出判定N數碼問題是否有唯一解的判定程序(HDOJ 3600):

  

  1  #include<iostream>
  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的上下移動即可。

  這里可以采用歸並排序來解決逆序對的問題,代碼如下(這里換一種方法,利用歸並排序來求逆序對的總數之和):

  

  1 #include<iostream>
  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 ==  0break;
 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來放送的。

 

 

 

---恢復內容結束---


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM