hdu4021——N数码问题及其扩展

在一个N*N的矩阵里,填入0,1,2直到N*N-1,当一个数的上下左右四个方向中有一个方向的相邻的数为0时,该数就可以移动到相应位置。给定一个初始矩阵和一个目标矩阵。问能否通过上述操作,将初始矩阵变换为目标矩阵。

这类问题主要是从逆序数奇偶性的角度去考虑,将矩阵内的数按照从左往右从上往下的顺序写成一个数列(0除外),然后求出这个数列的逆序数(数对两数为降序排列的数对总数)。对于左右移操作,他们不改变逆序数,对于上下移操作,若N-1为偶数,不改变奇偶性,若为奇数则改变奇偶性。因此若N-1为偶数,只要目标矩阵与初始矩阵逆序数相同既可以实现变换。若N-1为奇数,则初始矩阵与目标矩阵中0的行数差(每移一行奇偶性变化一次)加初始逆序数若与目标逆序数同奇偶,则可以完成转换。

就这个题而言,还多了一步。由于八个角为死角,别的数移不进去,所以先将里面的零移出,如果这以后八个死角的值不相等则肯定不能完成转换。若想等,则转化为N数码问题(即上述)。

代码如下:

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int Hash1[8]={0,1,2,7,16,21,22,23};
map<int,int> Hash2;
int S1[8],T1[16],S2[8],T2[16];
void swap(int& a,int& b){
    int c=a;a=b;b=c;
}
void read(int* a,int* b){
    for(int i=0,j=0,k=0;i<24;++i){
        if(Hash2.find(i)==Hash2.end()) scanf("%d",&b[j++]);
        else scanf("%d",&a[k++]);
    }
}
void deal(int *a,int* b){
    for(int i=0;i<8;++i){
        if(a[i]!=0) continue;
        switch(i){
            case 0:case 2:swap(a[i],b[0]);break;
            case 1:case 3:swap(a[i],b[3]);break;
            case 4:case 6:swap(a[i],b[12]);break;
            case 5:case 7:swap(a[i],b[15]);break;
        }
    }
}
int main(){
    Hash2[0]=0;Hash2[1]=1;Hash2[2]=2;Hash2[7]=3;
    Hash2[16]=4;Hash2[21]=5;Hash2[22]=6;Hash2[23]=7;
    int t,cnt1,cnt2,r1,r2;scanf("%d",&t);
    bool flag;
    while(t--){
        flag=true;cnt1=cnt2=0;
        read(S1,T1);read(S2,T2);
        deal(S1,T1);deal(S2,T2);
        for(int i=0;i<8;++i){
            if(S1[i]!=S2[i]){flag=false;break;}
        }
        if(!flag){printf("Y\n");continue;}
        for(int i=0;i<16;++i){
            for(int j=i+1;j<16;++j){
                if(T1[i]!=0&&T1[j]!=0&&T1[i]>T1[j]) ++cnt1;
                if(T2[i]!=0&&T2[j]!=0&&T2[i]>T2[j]) ++cnt2;
            }
        }
        for(int i=0;i<16;++i){
            if(T1[i]==0) r1=i/4;
            if(T2[i]==0) r2=i/4;
        }
        if((cnt1+cnt2+r1-r2)%2) printf("Y\n");
        else printf("N\n");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值