奇数码问题(求证明)

题意

题意

题解

艹,推逆序对推错了,艹

咳咳,依旧是两种做法。

做法1

考虑把整个图化成一条数列。(第二行拼到第一行后面,以此类推)

考虑整个图的逆序对数(\(0\)也考虑上)。

左右交换会使逆序对数\(+/-1\),那么上下交换呢?

由于\(0\)比所有数字都要小,所以肯定会\(+-n\)

那么\(x\)在数列和中间的\(n-1\)个数字会产生多少个逆序对数呢,假设少了\(q\)个逆序对,多了\(p\)个逆序对?

那么\(q+p\)为偶数,\(p-q\)必定为偶数,又因为\(n\)为奇数,所以上下交换也是一个奇数。

那岂不是意味着每次交换逆序对数就会改变,那拿什么判断呢?

但是不难发现\(0\)的移动也是个奇数,两个一加就是偶数,所以只需要把整个图的逆序对数加上\(0\)的位置即可。

但是至于为什么奇偶性质相同一定能到达,我也不知道,求证明。

#include<cstdio>
#include<cstring>
#define  N  510000
using  namespace  std;
int  bst[N];
int  a[N],b[N],nn;
inline  int  lowbit(int  x){return  x&-x;}
void  ins(int  x)
{
	while(x<=nn)
	{
		bst[x]++;
		x+=lowbit(x);
	}
}
int  findans(int  x)
{
	int  ans=0;
	while(x>=1)
	{
		ans+=bst[x];
		x-=lowbit(x);
	}
	return  ans;
}
int  n;
int  main()
{
	while(scanf("%d",&n)!=EOF)
	{
		memset(bst,0,sizeof(bst));
		nn=n*n;
		int  t=0;
		long  long  an1=0,an2=0;
		for(int  i=1;i<=n;i++)
		{
			for(int  j=1;j<=n;j++)
			{
				scanf("%d",&a[t+j]);
				a[t+j]++;
				if(a[t+j]==1)an1+=t+j;
			} 
			t+=n;
		}
		for(int  i=1;i<=nn;i++)
		{
			an1+=i-1-findans(a[i]);ins(a[i]);
		}
		memset(bst,0,sizeof(bst));
		t=0;
		for(int  i=1;i<=n;i++)
		{
			for(int  j=1;j<=n;j++)
			{
				scanf("%d",&a[t+j]);
				a[t+j]++;
				if(a[t+j]==1)an2+=t+j;
			} 
			t+=n;
		}
		for(int  i=1;i<=nn;i++)
		{
			an2+=i-1-findans(a[i]);ins(a[i]);
		}
		if((an1&1)==(an2&1))printf("TAK\n");
		else  printf("NIE\n");
	}
	return  0;
}

做法2

参考

判断忽略\(0\)之后的逆序对数是否相同,相同则可以到达,反之不能。

推法和上面差不多,我就不说了。

#include <bits/stdc++.h>
using namespace std;
const int N=510;
int n,m,a[N*N],b[N*N],c[N*N],i,j,k;
long long cnt;
void merge(int a[],int l,int r)
{
    if (r-l<1)
        return ;
    int mid=(l+r)>>1;
    merge(a,l,mid);
    merge(a,mid+1,r);
    int i=l,j=mid+1;
    for (int k=l; k<=r; k++)
    {
        if (j>r || i<=mid && a[i]<=a[j])
            b[k]=a[i++];
        else
        {
            cnt+=mid-i+1;
            b[k]=a[j++];
        }
    }
    for (int k=l; k<=r; k++)
        a[k]=b[k];
}
signed main()
{
    ios::sync_with_stdio(false);
//  freopen("stdin.in","r",stdin);
//  freopen("stdout.out","w",stdout);
    while(cin>>n)
    {
        int ok=0,x;
        for (i=1; i<=n*n; i++)
        {
            cin>>x;
            if (x==0)
                ok=1;
            else
                a[i-ok]=x;
        }
        ok=0;
        for (i=1; i<=n*n; i++)
        {
            cin>>x;
            if (x==0)
                ok=1;
            else
                c[i-ok]=x;
        }
        cnt=0;
        memset(b,0,sizeof(b));
        merge(a,1,n*n);
        long long ans=cnt;
        memset(b,0,sizeof(b));
        cnt=0;
        merge(c,1,n*n);
        if ((ans&1)==(cnt&1))
            puts("TAK");
        else
            puts("NIE");
    }
    return 0;
}

作者:秦淮岸灯火阑珊已退役
链接:https://www.acwing.com/solution/content/847/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2020-07-31 14:09  敌敌畏58  阅读(206)  评论(0编辑  收藏  举报