POJ 2893 M × N Puzzle(逆序对-BIT)

Description
8数码问题的升级,就是通过移动空格(用0代替)使得原来状态变成有序的1234……0,不过,这题是m*n数码
Input
多组输入,每组用例第一行为两个整数m和n表示数码行列数,之后为一m*n矩阵表示m*n数码,以m=n=0结束输入
Output
对于每组用例,若能够通过移动空格将数码变为有序状态则输出YES,否则输出NO
Sample Input
3 3
1 0 3
4 2 5
7 8 6
4 3
1 2 5
4 6 9
11 8 10
3 7 0
0 0
Sample Output
YES
NO
Solution
某状态的奇偶性定义为逆序对(不包括0的)总数的奇偶性。 此题目终态为偶数
首先,0的左右移动不改变奇偶性;然后是0的上下移动,如下:
————-0***********
***********x————-
x是任意数,现在要把x移上去,那么***********中,假设有a个大于x,b个小于x,那么移动之后逆序数就会加上一个b-a,x所能影响的也就是这些,除此之外,其他都不变。
接着,如果列数为偶数,那么***********的个数就是奇数,b,a奇偶性互异,b-a为奇数,所以移动一次后,原序列的逆序数的奇偶性变了。考虑到最后0会移动到最后一行,所以奇偶性会改变n-i次(i为0的行数),只需判断最后是否是偶数即可。
反之,如果列数为奇数,那么***********的个数就是偶数,b,a奇偶性相同,b-a为偶数,所以移动一次后,原序列的逆序数的奇偶性没变。因为无论怎么移,奇偶性都不变,所以说一开始初态的奇偶性就必须与末态一致
所以得出如下结论:
n为奇数,0上下移动不改变奇偶性,故逆序数为偶的YES
n为偶数,0上下移动逆序数变化为±1,此时还要考虑0的竖直距离,逆序数%2 == 距离%2 时YES
Code

#include<stdio.h>
#include<string.h>
#define maxn 1000001
int m,n,a[maxn],b[maxn],k;
void add(int i)
{
    while(i<=k)
    {
        b[i]++;
        i+=i&-i;
    }
}
int sum(int i)
{
    int res=0;
    while(i>0)
    {
        res+=b[i];  
        i-=i&-i;
    }
    return res;
}
int solve()
{
    int res=0;
    for(int i=0;i<k;i++)
    {
        add(a[i]);
        res+=i+1-sum(a[i]);
    }
    return res;
}
int main()
{
    while(scanf("%d%d",&m,&n),m)
    {
        memset(b,0,sizeof(b));//初始化 
        int d,step;
        k=0;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
            {
                scanf("%d",&d);
                if(d==0)//逆序数要考虑0的竖直位置 
                    step=m-i-1;
                else
                    a[k++]=d;
            }
        int ans=solve();//BIT求逆序对数 
        if(n&1)//n为奇数时不需要考虑0的竖直位置 
            step=0;
        printf(step%2==ans%2?"YES\n":"NO\n"); 
    }
    return 0;
}

你可能感兴趣的:(POJ 2893 M × N Puzzle(逆序对-BIT))