首页 > ACM题库 > HDU-杭电 > HDU 4021-24 Puzzle[解题报告]HOJ
2015
04-15

HDU 4021-24 Puzzle[解题报告]HOJ

24 Puzzle

问题描述 :

Daniel likes to play a special board game, called 24 puzzle. 24 puzzle is such a game that there are tiles with the number 1 to 23 in a play board like the follow picture:
Ads Proposal

  The ‘#’ denotes the positions that the tiles may be placed on. There are 24 possible positions in total, so one of them is not occupied by the tile. We can denote the empty position by zero.
  Daniel could move the tiles to the empty position if the tile is on the top, bottom, left or right of the empty position. In this way Daniel can reorder the tiles on the board.
Usually he plays with this game by setting up a target states initially, and then trying to do a series of moves to achieve the target. Soon he finds that not all target states could be achieved.
  He asks for your help, to determine whether he has set up an impossible target or not.

输入:

The first line of input contains an integer denoting the number of test cases.
  For each test case, the first line contains 24 integers denoting the initial states of the game board. The numbers are the describing the tiles from top to bottom, left to right. And the empty position is indicated by zero. You can assume that the number of each tile are different, and there must be exactly one empty position. The second line of test case also contains 24 integers denoting the target states.

输出:

The first line of input contains an integer denoting the number of test cases.
  For each test case, the first line contains 24 integers denoting the initial states of the game board. The numbers are the describing the tiles from top to bottom, left to right. And the empty position is indicated by zero. You can assume that the number of each tile are different, and there must be exactly one empty position. The second line of test case also contains 24 integers denoting the target states.

样例输入:

2
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
3 1 2 0 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
3 0 2 1 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

样例输出:

N
Y

http://acm.hdu.edu.cn/showproblem.php?pid=4021   题目连接

扩展八数码的讨论   转自  http://zhyu.me/acm/hdu-4021.html

题意:给出一个board,上面有24个位置,其中23个位置上放置了标有数字1~23的方块,一个为空位(用数字0表示),现在可以把空位与它旁边的方块交换,给出board的起始状态,问是否可以达到指定的状态。

思路:看起来很像著名的“八数码”问题,首先,针对八个特殊位置(死角),如果这里有空位就把它和相邻的位置交换,这样之后如果两个状态的对应死角上的数字不同,那么显然是不能达到指定状态的,因为无法把死角处的数字换出去。

搞定了死角后就只剩下4×4的board了,这就变成了八数码问题的拓展——15数码。首先想想八数码是如何判断有解的:首先把所有数字(不包括空位的0)写成一行,就得到了一个1~8的排列,考虑空位的交换情况:1.左右交换,2.上下交换。对于左右交换而言,是不会改变写出的排列的逆序数的;而对上下交换,相当于在排列中向前或向后跳了两个位置,那么要么两个数都比它大或小,这样逆序数加2或减2,要么两个数一个比它大一个比它小,这样逆序数不变,综上,对于八数码问题,操作不会改变逆序数的奇偶性,所以只有初始状态和指定状态的逆序数奇偶性相同就有解。

弄清楚了八数码,扩展起来就容易了,现在我们将其扩展到N维(即N*N的board,N*N-1数码问题)。

首先无论N的奇偶,左右交换不改变逆序数,N为奇数时:上下交换逆序数增加N-1或减少N-1或不变,因为N为奇数,所以逆序数奇偶性不变;而N为偶数时:上下交换一次奇偶性改变一次。

结论:N为奇数时,初始状态与指定状态逆序数奇偶性相同即有解;N为偶数时,先计算出从初始状态到指定状态,空位要移动的行数m,如果初始状态的逆序数加上m与指定状态的逆序数奇偶性相同,则有解。

好了,现在这道题就简单了,计算逆序数和空格要移动的行数即可。

#include<stdio.h>
#include<algorithm>
using namespace std;
int n,q[]={0,1,3,2,8,17,23,22,24},e[]={0,4,4,7,7,18,18,21,21};
int abs(int x){
	return x>0?x:(-x);
}
int main(){
	scanf("%d",&n);
	while(n--){
		int begin[33],end[33];
		for(int i=1;i<=24;i++)
			scanf("%d",&begin[i]);
		for(int i=1;i<=24;i++)
			scanf("%d",&end[i]);
		for(int i=1;i<=8;i++){
			if(begin[q[i]]==0)
				swap(begin[q[i]],begin[e[i]]);
			if(end[q[i]]==0)
				swap(end[q[i]],end[e[i]]);
		}
		bool flag=0;
		for(int i=1;i<=8;i++){
			if(begin[q[i]]!=end[q[i]]){
				flag=1;
				break;
			}
		}
		if(flag){
            printf("Y\n");
			continue;
		}
		int n1=0,n2=0,m_pos1=0,m_pos2=0;
		int a[33],b[33],sum1=0,sum2=0;
		for(int i=1;i<=24;i++){
			flag=0;
			for(int j=1;j<=8;j++)
				if(i==q[j])
					flag=1;
			if(flag)continue;
			a[sum1++]=begin[i];
		}
        for(int i=1;i<=24;i++){
			flag=0;
			for(int j=1;j<=8;j++)
				if(i==q[j])
					flag=1;
			if(flag)continue;
			b[sum2++]=end[i];
		}
		for(int i=1;i<sum1;i++){
			if(a[i]==0)
				m_pos1=i;
			else{
				for(int j=0;j<i;j++){
					if(a[i]<a[j])
					    n1++;
				}
			}
		}
		for(int i=1;i<sum2;i++){
			if(b[i]==0)
			    m_pos2=i;
			else{
				for(int j=0;j<i;j++){
					if(b[i]<b[j])
					    n2++;
				}
			}
		}
		int it=abs(m_pos1/4-m_pos2/4);
		if(abs(it+n1-n2)%2==0)
			printf("N\n");
		else
			printf("Y\n");
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

参考:http://blog.csdn.net/onepiecehuiyu/article/details/8750827