奇数码问题--->M×N Puzzle

AcWing

奇数码游戏是八数码问题的一个扩展,在一个n×n的网格中进行,其中n为奇数,1个空格和1~(n^2-1)(n^2-1)个数恰好不重不漏地分布在n×n的网格中.空格移动的规则与八数码游戏相同,实际上,八数码就是一个n=3的奇数码游戏.现在给定两个奇数码游戏的局面,请判断是否存在一种移动空格的方式,使得其中一个局面可以变化到另一个局面。

分析:奇数码游戏两个局面可达,当且仅当两个局面下网格中的数依次写成1行(n*n-1)个元素的序列后(不考虑空格,也就是0),逆序对个数的奇偶性相同.证明请自行参考网上资料.所以我们只要求两次逆序对的个数,然后判断奇偶性是否相同就行了.

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=250005;
int ans1,ans2,a[N],b[N],c[N];
inline void msort1(int l,int r){
    if(l==r)return;
    int mid=(l+r)/2;
    msort1(l,mid);msort1(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(a[i]<=a[j]){b[k]=a[i];k++;i++;}
        else{
            b[k]=a[j];k++;j++;
            ans1+=mid-i+1;
        }
    }
    while(i<=mid){b[k]=a[i];k++;i++;}
    while(j<=r){b[k]=a[j];k++;j++;}
    for(int i=l;i<=r;++i)a[i]=b[i];
}
inline void msort2(int l,int r){
    if(l==r)return;
    int mid=(l+r)/2;
    msort2(l,mid);msort2(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(c[i]<=c[j]){b[k]=c[i];k++;i++;}
        else{
            b[k]=c[j];k++;j++;
            ans2+=mid-i+1;
        }
    }
    while(i<=mid){b[k]=c[i];k++;i++;}
    while(j<=r){b[k]=c[j];k++;j++;}
    for(int i=l;i<=r;++i)c[i]=b[i];
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		int tot1=0,tot2=0;
		for(int i=1;i<=n*n;++i){
			int x=read();
			if(x)a[++tot1]=x;
		}
		for(int i=1;i<=n*n;++i){
			int x=read();
			if(x)c[++tot2]=x;
		}
		if(n==1){puts("TAK");continue;}
		ans1=0;ans2=0;
		msort1(1,tot1);msort2(1,tot2);
		if(((ans1&1)&&(ans2&1))||(!(ans1&1)&&!(ans2&1)))puts("TAK");
		else puts("NIE");
	}
    return 0;
}

POJ

本题是奇数码问题的拓展,网格变成(n*m),奇偶性不确定,且其中一个局面的逆序对个数为0.

我们只要求一个局面的逆序对个数,然后根据列数奇偶性分情况讨论:

1.若列数m为奇数,同上题,逆序对个数奇偶性相同即可,而其中一个局面已知逆序对个数为0,也就是偶数了.

2。若列数m为偶数,这次一个局面“逆序对个数+两个局面下空格之间的行数之差”与另一个局面的逆序对个数奇偶性相同即可.其中已知那个逆序对个数为0的局面的空格0在第n行.

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1000005;
int ans,a[N],b[N];
inline void msort(int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	msort(l,mid);msort(mid+1,r);
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r){
		if(a[i]<=a[j])b[k++]=a[i++];
		else{
			b[k++]=a[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid)b[k++]=a[i++];
	while(j<=r)b[k++]=a[j++];
	for(int i=l;i<=r;++i)a[i]=b[i];
}
int main(){
	while(1){
		int n=read(),m=read();if(!n&&!m)break;
		int tot=0,pos;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j){
				int x=read();
				if(x)a[++tot]=x;
				else pos=i;
			}
		ans=0;msort(1,tot);
		if(m&1){
			if(ans&1)puts("NO");
			else puts("YES");
		}
		else{
			if((ans+n-pos)&1)puts("NO");
			else puts("YES");
		}
	}
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11253917.html