M×NPuzzle 奇数码问题,逆序对,归并排序

题意

给定 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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值