题意
给定 m×n
( m,n 为整数, 2⩽m,n⩽999 )的棋盘,上面有 m×n−1 个数和一个空格。给出一个局面,每次仅允许交换空格和其四周相邻的一个数(如果有的话),问是否有办法将这个局面移动为目标局面(空格在最后一行最后一列,其余格 (i,j) 上的数为 (i−1)∗m+j )。
思路:将二维降至一维,将每个数字按1-m*n-1的顺序写成一行。m*n网格根据列数奇偶性判断 。如果列数n为奇数 ,那n-1为偶数,又因为题解条件下末局面逆序对个数为0,所以只需判断初局面是否为偶 。如果列数为偶数,那么就看逆序对数之差和两个局面下空格所在行数之差奇偶性是否相同 。
归并排序求逆序对模板:
void merge(ll a[],ll l, ll r){
if(l>=r) return;
ll mid=(l+r)>>1;
merge(a,l,mid);
merge(a,mid+1,r);
ll i=l,j=mid+1;
for(ll k=l;k<=r;k++){
if(j>r||i<=mid&&a[i]<=a[j]) b[k]=a[i++];
else{
ans+=mid-i+1;
b[k]=a[j++];
}
}
for(ll k=l;k<=r;k++) a[k]=b[k];
}
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e8;
ll ans, m,n,p,num,a[maxn],b[maxn];//b为临时数组
void merge(ll a[],ll l, ll r){
if(l>=r) return;
ll mid=(l+r)>>1;
merge(a,l,mid);
merge(a,mid+1,r);
ll i=l,j=mid+1;
for(ll k=l;k<=r;k++){
if(j>r||i<=mid&&a[i]<=a[j]) b[k]=a[i++];
else{
ans+=mid-i+1;
b[k]=a[j++];
}
}
for(ll k=l;k<=r;k++) a[k]=b[k];
}
int main(){
while(1){
cin>>m>>n;
num=m*n-1;//数字个数
int k=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
char temp;
cin>>temp;
if(temp=='_'){
p=i;//记录空格所在行数
}
else a[k++]=temp-'0';
}
}
p=m-p;//空格前后两局面所在行数之差
ans=0;//清0
fill(b,b+maxn,0);//清0
merge(a,1,num);//归并排序;
//m*n网格根据列数奇偶性判断
if(n&1){//如果列数n为奇数 ,那n-1为偶数,又因为题解条件下末局面逆序对个数为0,所以只需判断初局面是否为偶
if(ans&1) puts("NO");//会自动换行
else puts("YES");
}
else{
//如果列数为偶数,那么就看逆序对数之差和两个局面下空格所在行数之差奇偶性是否相同
if((p+ans)&1) puts("NO");//奇数加奇数=偶数,偶数+偶数=偶数
else puts("YES");
}
}
return 0;
}