蓝书(算法竞赛进阶指南)刷题记录——CH0503 奇数码问题及其证明

版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82081536

题目:0503 奇数码问题.

题目大意:给出两个奇数码的状态,判断这两个奇数码能否互相转换.

这道题的结论是把每一行都顺序写出,若两个序列的逆序对数奇偶性相同,则它们之间可以互相转换.

证明如下:

我们把空格左右移动,则逆序对数不会改变.

我们把空格上下移动,则空格原来和现在的位置之间只隔了n-1个位置,n-1是偶数.

这样上下转换相当于让这个数前或后的n-1个数调到了后或前面,逆序对数奇偶性自然不会变.

证毕.

实现的话就把这个方阵拆成一个一维数组然后跑归并逆序对或者树状数组逆序对就可以了. 

那么代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500;
int n,a[N*N+9],b[N*N+9],top,r[N*N+9];
LL ans1,ans2;
LL mergea(int L,int R){
  if (R<=L) return 0LL;
  int mid=L+R>>1,i=L-1,j=mid,k=-1;
  LL ans=mergea(L,mid)+mergea(mid+1,R);
  while (i<mid&&j<R)
    if (a[i+1]<a[j+1]) r[++k+L]=a[++i];
    else ans+=LL(i-L+1),r[++k+L]=a[++j];
  while (i<mid)
    r[++k+L]=a[++i];
  while (j<R)
    ans+=LL(i-L+1),r[++k+L]=a[++j];
  for (int i=L;i<=R;i++)
    a[i]=r[i];
  return ans;
}
LL mergeb(int L,int R){
  if (R<=L) return 0LL;
  int mid=L+R>>1,i=L-1,j=mid,k=-1;
  LL ans=mergeb(L,mid)+mergeb(mid+1,R);
  while (i<mid&&j<R)
    if (b[i+1]<b[j+1]) r[++k+L]=b[++i];
    else ans+=LL(i-L+1),r[++k+L]=b[++j];
  while (i<mid)
    r[++k+L]=b[++i];
  while (j<R)
    ans+=LL(i-L+1),r[++k+L]=b[++j];
  for (int i=L;i<=R;i++)
    b[i]=r[i];
  return ans;
}
Abigail into(){
  top=0;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++){
      scanf("%d",&a[++top]);
      if (!a[top]) top--;
    }	 
  top=0;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++){
      scanf("%d",&b[++top]);
      if (!b[top]) top--;
    }
  n=n*n-1;
}
Abigail work(){
  ans1=mergea(1,n);
  ans2=mergeb(1,n);
}
Abigail outo(){
  if (ans1&1^ans2&1) printf("NIE\n");
  else printf("TAK\n");
}
int main(){
  int T=1;
  while (~scanf("%d",&n)){
    into();
    work();
    outo();
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82081536