hdoj 3600 Simple Puzzle

题目链接: hdoj 3600 Simple Puzzle
分析:

本题是八数码解存在性问题的扩展。至于解的存在的问题转换为逆序对的奇偶性问题(具体证明略)。起始状态的排列的逆序对若与目标状态的逆序对奇偶性相同则可以存在解。求逆序对的方法可以用线段树或者归并排序。而逆序对的考虑除了排列本身(去掉空格0)还有空格0也会影响其奇偶性,具体影响方式为,若数码是奇数列则直接判断始末状态逆序对的奇偶性是否相等。
若是偶数列,则判断 末状态逆序对 与 始状态逆序对+空格的垂直偏移量是否奇偶性相同。(证明再次略过,可参考其他博客)。

代码:

#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<queue>
#include<set>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[90000],t[90000],ans;
void Ms(int l,int m,int r)
{
	int i=l;
	int j=m+1;
	int k=l;
	while(i<=m&&j<=r){
		if(a[i]>a[j]){
			ans+=m-i+1;
/*将两个有序的排列合并时
	前面排列的a[i]如果大于后面排列种的元素a[j],则说明对于a[j]来说,前面有m-i+1个比它大的数。
	有人会担心排序过程中改变了排列导致逆序数会改变,
	仔细想想,二分的归并排序在这里才开始改变元素位置,
	但a[j]前面到底有多少个更大的数呢(排列后半段在a[j]前的都没它大),而排列的前半段(有序)的排列顺序不会有影响,
	对于每个元素都是如此。*/
			 
			t[k++]=a[j++];
		}
		else
			t[k++]=a[i++];
	}
	while(i<=m)t[k++]=a[i++];
	while(j<=r)t[k++]=a[j++];
	for(i=l;i<=r;i++)
		a[i]=t[i];	
}
void Merge(int l,int r)
{
	if(l<r){
		int m=(l+r)/2;
		Merge(l,m);
		Merge(m+1,r);
		Ms(l,m,r);
	}
}
int main()
{
	freopen("hdoj3600.txt","r",stdin);
	int n,num,r,c,k=0;
	while(cin>>n){
		if(n==0)break;
		k=0;
		ans=0;
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++){
				cin>>num;
				if(num==0)
					r=i;
				else
					a[k++]=num;
			}
		Merge(0,k-1);
		if(n%2==1){//奇数列且目标与起始状态的奇偶性相同  
			if(ans%2==0) 
				cout<<"YES"<<endl;
			else
				cout<<"NO"<<endl;
		}
		else {
			if((ans+n-1-r)%2==0)//偶数列但是空格的始末上下距离差与逆序数的和为偶数
				cout<<"YES"<<endl;
			else
				cout<<"NO"<<endl; 
		}		
	}	            
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值