zoukankan      html  css  js  c++  java
  • $POJ2893$ $M imes N$ $Puzzle$

    链接

    背景

    这题其实是 奇数码问题, (AcWing108/CH0503) 的原版, (POJ) (Monthly) (2006.7.30) (H)题, (POJ2893)

    题意

    给定 (m imes n)(m,n) 为整数, (2 leqslant m,n leqslant 999) )的棋盘,上面有 (m imes n-1) 个数和一个空格。给出一个局面,每次仅允许交换空格和其四周相邻的一个数(如果有的话),问是否有办法将这个局面移动为目标局面(空格在最后一行最后一列,其余格 ((i,j)) 上的数为 ((i-1)*m+j) )。

    解法

    将棋盘上的数一行一行取出排成一列(空格不取),可以得到一个序列。
    思考操作的数学意义。左右交换空格和数字对该序列的逆序对数毫无影响。而上下交换空格和数字则会交换序列中 (n-1) 个数的位置。又由于 (n) 为整数,所以 (n-1) 需要分类讨论。
    设交换前有 (a) 个数小于交换的数 (x) ,有 (b) 个数大于 (x)(a+b=n-1)(a ot= b) )。则交换前交换的数的逆序对数为 (a) ,交换后交换的数的逆序对数为 (b) 。若 (a < b) ,则逆序对数增加了 (b-a) ,否则逆序对数减少了 (a-b)
    (n) 为奇数时, (n-1) 为偶数,则交换前后逆序对数的变化一定为偶数。两个局面能够从一个操作成另一个当且仅当两个局面对应的序列的逆序对数奇偶性相同,否则一定不能转化。奇偶性相同为充要条件,其必要性十分容易说明,但充分性 (cdots cdots) 具体证明过程较长,这里不再展开。又因为目标局面的逆序对数为 (0) ,所以原序列的逆序对数为奇数时,不能达成目标,否则可以。
    (n) 为偶数时, (n-1) 为奇数,每次交换前后逆序对数的变化一定为奇数。算出上下交换的次数(原局面和完美局面空格相差的行数) (p) 。设原序列的逆序对数为 (q) ,当 (p)(q) 奇偶性相同时,可以达成目标,否则不能。
    然后对原局面归并排序求逆序对数即可。时间复杂度为 (O(n^2)) 。(瓶颈在读入)树状数组求逆序对的时间复杂度是 (O((n+m)) (log) (n))(m) 为数值范围大小),本题中 (m = 998001) ,故也可以通过本题。喜欢写哪个就写哪个呗 (QAQ)

    (trick)

    (1.) 先把情况分析讨论清楚再开始写 (code)

    (2.) 目标局面显然逆序对为 (0) ,无需建序列求逆序对。

    细节

    (1.) 题面的网格为 (m imes n) 而不是 (n imes m) 。(好傻逼的错误啊)

    代码

    我也不知道怎么肥四,反正这篇 (blog) 的代码框就是合不了,然后一堆 (bug) 神马的 (cdots cdots) ,所以就不收起来了,泥萌自己看吧 (QwQ)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int n,m,sp,tmp,k,rr,a[998005],b[998005];
    long long ans;
    void msort(int l,int r,int mid)
    {
    	if(l==r)
    		return;
    	msort(l,mid,(l+mid)>>1);
    	msort(mid+1,r,(mid+1+r)>>1);
    	int i=l,j=mid+1;
    	for(register int k=l;k<=r;k++)
    	{
    		if(j>r||(i<=mid&&a[i]<a[j]))
    			b[k]=a[i++];
    		else
    		{
    			ans+=mid-i+1;
    			b[k]=a[j++];
    		}
    	}
    	for(register int k=l;k<=r;k++)
    		a[k]=b[k];
    }
    int main()
    {
    	while(1)
    	{
    		m=read();
    		n=read();
    		if((!m)&&(!n))
    			break;
    		k=0;
    		for(register int i=1;i<=m;i++)
    		{
    			for(register int j=1;j<=n;j++)
    			{
    				tmp=read();
    				if(tmp)
    					a[++k]=tmp;
    				else
    					sp=i;
    			}
    		}
    		sp=m-sp;
    		ans=0;
    		rr=m*n-1;
    		msort(1,rr,(1+rr)>>1);
    		if(n&1)
    		{
    			if(ans&1)
    				puts("NO");
    			else
    				puts("YES");
    		}
    		else
    		{
    			if((sp+ans)&1)
    				puts("NO");
    			else
    				puts("YES");
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    css3新单位vw、vh、vmin、vmax的使用介绍
    vue中的css作用域、vue中的scoped坑点
    vue组件中的样式属性:scoped,解决在父组件中无法修改子组件样式问题
    修改elementUI组件样式无效的问题研究
    Javascript里面的时间处理:将时间戳或时间对象转成字符串格式
    详解vue父组件传递props异步数据到子组件的问题
    vue父组件异步传递prop到子组件echarts画图问题踩坑总结
    父组件中调用子组件的方法和属性
    Echarts使用dataset数据集管理数据
    Echarts的legend改变图例图标为自定义图片
  • 原文地址:https://www.cnblogs.com/Peter0701/p/11250950.html
Copyright © 2011-2022 走看看