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

  在前面,有说用各种搜索方法(后面还将给出网友整理的八数码问题的十重境界) 来解决8 PUZZLE和15 PUZZLE问题。实际上,由于拼图游戏的种类繁多,我们可以延拓到两种比较特殊的类型:(1)M*N PUZZLE(2)N PUZZLE,(1)中的M*N当然是任意的正整数,而(2)中的N,这里指代的是一个正整数的平方和减一所得到的值。

  吴昊品游戏核心算法 Round 17 —— M*N PUZZLE 与 N PUZZLE 的解的唯一性定理(由特殊到一般)_第1张图片

  如图所示,这是一个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  }

  进一步推广,魔方,七巧板,华容道……

  吴昊品游戏核心算法 Round 17 —— M*N PUZZLE 与 N PUZZLE 的解的唯一性定理(由特殊到一般)_第2张图片 

  如图,这是一个魔方,是由N*N*N个小立方体组成的。我们这里随意拿走一个小立方体,并假设整个结构没有被破坏。那么,这是一次更普遍的飞跃,由二维空间上升到了三维空间。但是,其解的唯一性定律还适用嘛?答案是肯定的,证明如下:

  魔方数码问题的解的唯一性定理

  考虑左右移动空格,逆序不变;同一层上下移动空格,跨过N-1个格子;上下层移动空格,跨过N^2-1个格子。

  当N为奇数时,N-1和N^2-1均为偶数,也就是任意移动空格逆序奇偶性不变。那么逆序奇偶性相同的两个状态可相互到达。

  当N为偶数时,N-1和N^2-1均为奇数,也就是令空格位置到目标状态空格位置的y z方向的距离之和,称为空格距离。若空格距离为偶数,两个逆序奇偶性相同的状态可相互到达;若空格距离为奇数,两个逆序奇偶性不同的状态可相互到达。

  七巧板

  更为一般性的拓展,我们可以考虑到其图形不一定是正方形或者是正方体,比如,我们国家民间的七巧板游戏,这些方块的形状都不是正方形,甚至彼此都不一定相 同,但是,经过一定的组合之后,却构成了一个比较好玩的游戏。但是,这个游戏并不是通过方块的滑动来实现的,而是放置,所以,在这一点上看,软件的实现比 较简单。

  吴昊品游戏核心算法 Round 17 —— M*N PUZZLE 与 N PUZZLE 的解的唯一性定理(由特殊到一般)_第3张图片

  其实现并不复杂,只要给出固定的解,每一次比对是否将图形安装到固定的位置就可以了,如图所示,这是其中的一款游戏:

  吴昊品游戏核心算法 Round 17 —— M*N PUZZLE 与 N PUZZLE 的解的唯一性定理(由特殊到一般)_第4张图片

  华容道

  更复杂的游戏,比如华容道的AI,就要考虑很多因素,因为,不同的方块形状是不一样的,不过,索性还有一定的规则可以追寻,其实现目前很多,我用的是一个中学老师的AI版本(他写成了一篇论文)。我之后会单独以一个Round来放送的。

  吴昊品游戏核心算法 Round 17 —— M*N PUZZLE 与 N PUZZLE 的解的唯一性定理(由特殊到一般)_第5张图片

 

 

---恢复内容结束---

你可能感兴趣的:(round)