POJ - 2893 M × N Puzzle (八数码问题是否有解)

本文深入探讨了八数码问题的解决策略,通过分析数组中每个数字在其当前位置前的较小数字数量,即逆序数,来判断初始状态是否有解。讨论了移动操作对逆序数奇偶性的影响,并结合空格的行位置,提出了一种有效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

就是通过移动空格(用0代替)使得原来状态变成有序的1234......0

分析:

以数组为一维的举例子.
将八数码的一个结点表示成一个数组a[9],空格用0表示,设临时函数p(x)定义为:x数所在位置前面的数比x小的数的个数,
其中0空格不算在之内,那设目标状态为b[9],那r=sigma(p(x)) sigma()表示取所有的x:1-8并求和,
那对于初始状态a[9],t=sigma(p(x)),如果r和t同为奇数或者同为偶数,那么该状态有解,否则无解。

考虑到四种移动方法对sigma(p(x))的影响,左移和右移是不会影响它的值的,
更不会影响奇偶性,如果是上移或者下移就会影响:
上移:一次上移会使一个元素向前跳两个数字的位置,设这两个数字为a1,a2
不妨设a1<a2,移的这个数字设为a0,那无非只有以下三次情况:
1,a0<a1<a2,考虑它们三者的p(x)值,p(a0)不变,p(a1)++,p(a2)++,总体增加了2
2,a1<a0<a2,p(a0)--,p(a1)不变,p(a2)++,总体不变
3,a1<a2<a0,p(a0)-=2,p(a1),p(a2)不变,总体减小了2

综合起来的结论就是不会影响sigma(p(x))的奇偶性。

接着考虑列数的奇偶。

-------------0***********

***********x-------------

x是任意数,现在要把x移上去,那么***********中,假设有a个大于x,b个小于x,那么移动之后逆序数就会加上一个b-a,x所能影响的也就是这些罢了,除此之外,其他都不变。

那么******的个数就是奇数,b,a奇偶性互异,b-a为奇数,所以移动一次后,原序列的逆序数的奇偶性变了。

考虑到最后0会移动到最后一行,所以奇偶性会改变n-i次(i为0的行数),只需判断最后是否是偶数即可。

反之,如果列数为奇数,那么******的个数就是偶数,b,a奇偶性相同,b-a为偶数,所以移动一次后,原序列的逆序数的奇偶性没变。

因为无论怎么移,奇偶性都不变,所以说一开始初态的奇偶性就必须与末态一致。

#include<cstdio>
#include<cstring>

using namespace std;

const int maxn=1e6+10;

int a[maxn],c[maxn];

int lowbit(int i){
    return i&(-i);
}

void insert(int i,int x){
    for(;i<maxn;i+=lowbit(i)){
        c[i]+=x;
    }
}

int getsum(int i){
    int sum=0;
    for(;i;i-=lowbit(i)){
        sum+=c[i];
    }
    return sum;
}

int main(){

    int n,m;
    while(scanf("%d%d",&n,&m)&&(n||m)){
        int x,sum=0,num=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int b;
                scanf("%d",&b);
                if(b){
                    a[num++]=b;
                }else x=n-i;
            }
        }
        memset(c,0,sizeof c);
        for(int i=num-1;i>=0;i--){
            sum+=getsum(a[i]-1);
            insert(a[i],1);
        }
        if(m&1){
            if(sum&1) printf("NO\n");
            else printf("YES\n");
        }else {
            if((x^sum)&1) printf("NO\n");
            else printf("YES\n");
        }
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值