poj2893 M*N puzzle 【n*m数码问题小结】By cellur925
这个问题是来源于lydrainbowcat老师书上讲排序的一个扩展。当时讲的是奇数码问题,其实这种问题有两种问法:一种局面能否到另一种局面、到达目标局面的最小步数。
本文部分内容引用于lydrainbowcat《算法竞赛进阶指南》。
一、判定问题是否有解
我们可以由简至难看这样几个问题:
1.
描述 你一定玩过八数码游戏,它实际上是在一个3*3的网格中进行的,1个空格和1~8这8个数字恰好不重不漏地分布在这3*3的网格中。 例如: 5 2 8 1 3 _ 4 6 7 在游戏过程中,可以把空格与其上、下、左、右四个方向之一的数字交换(如果存在)。 例如在上例中,空格可与左、上、下面的数字交换,分别变成: 5 2 8 5 2 _ 5 2 8 1 _ 3 1 3 8 1 3 7 4 6 7 4 6 7 4 6 _ 奇数码游戏是它的一个扩展,在一个n*n的网格中进行,其中n为奇数,1个空格和1~n*n-1这n*n-1个数恰好不重不漏地分布在n*n的网格中。 空格移动的规则与八数码游戏相同,实际上,八数码就是一个n=3的奇数码游戏。 现在给定两个奇数码游戏的局面,请判断是否存在一种移动空格的方式,使得其中一个局面可以变化到另一个局面。
奇数码问题两个问题可达,当且仅当他们网格中的数写成不含空格的序列后,两个序列的逆序对数的奇偶性相同。
(证明就不证了qwq)
2.
诸如此题,n*n的网格,只不过n是偶数。
这时候两局面可达当且仅当两序列的(逆序对数+两局面空格间行数差)的奇偶性相同
3.
扩展到n*m?
实际上对于是否有解关键的判定方法取决于列数。
当m为偶数 参考偶数码问题 求解
当n为偶数 参考奇数码问题 求解
本题就可以轻松愉悦地求解了==。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 7 int n,m,pos,x,ans,zero; 8 int seq[1000090],tmp[1000090]; 9 10 void msort(int l,int r) 11 { 12 if(l==r) return ; 13 int mid=(l+r)>>1; 14 msort(l,mid); 15 msort(mid+1,r); 16 int i=l,j=mid+1,k=l-1; 17 while(i<=mid&&j<=r) 18 { 19 if(seq[i]<=seq[j]) 20 tmp[++k]=seq[i],i++; 21 else tmp[++k]=seq[j],j++,ans+=mid-i+1; 22 } 23 while(i<=mid) 24 tmp[++k]=seq[i],i++; 25 while(j<=r) 26 tmp[++k]=seq[j],j++; 27 for(int qwq=l;qwq<=r;qwq++) 28 seq[qwq]=tmp[qwq]; 29 } 30 31 int main() 32 { 33 while(scanf("%d%d",&n,&m)!=EOF&&n!=0) 34 { 35 if(n==0) break; 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=m;j++) 38 { 39 scanf("%d",&x); 40 if(x) seq[++pos]=x; 41 else zero=n-i; 42 } 43 msort(1,pos); 44 if(m&1) 45 zero=0; 46 if((ans+zero)%2==0) 47 printf("YES\n"); 48 else printf("NO\n"); 49 ans=0;pos=0;zero=0; 50 } 51 return 0; 52 }
开始在主程序中调用msort(1,pos)时出锅了,写成了(1,n),这zz错误也是让我无话可说....。
二、求解最小步数
这类问题一般采用bfs解决,好像还有A*哈希的方法,可是我太菜了不会.....
例题1 Luogu P1379 八数码难题
例题2 Luogu P2730魔板【USACO Trianing】
这个其实是一种变体了qwq。
独立意志与自由思想是必须争的,且须以生死力争。