poj2893 & zoj2004(八数码是否有解 树状数组)

M × N Puzzle
Time Limit: 4000MS Memory Limit: 131072K
Total Submissions: 4206 Accepted: 1163

Description

The Eight Puzzle, among other sliding-tile puzzles, is one of the famous problems in artificial intelligence. Along with chess, tic-tac-toe and backgammon, it has been used to study search algorithms.

The Eight Puzzle can be generalized into an M × N Puzzle where at least one of M and N is odd. The puzzle is constructed with MN − 1 sliding tiles with each a number from 1 to MN − 1 on it packed into aM by N frame with one tile missing. For example, with M = 4 and N = 3, a puzzle may look like:

162
403
759
10811

Let's call missing tile 0. The only legal operation is to exchange 0 and the tile with which it shares an edge. The goal of the puzzle is to find a sequence of legal operations that makes it look like:

123
456
789
10110

The following steps solve the puzzle given above.

START

162
403
759
10811

DOWN

102
463
759
10811
LEFT
120
463
759
10811

UP

123
460
759
10811

 

RIGHT

123
406
759
10811

UP

123
456
709
10811
UP
123
456
789
10011

LEFT

123
456
789
10110

GOAL

Given an M × N puzzle, you are to determine whether it can be solved.

Input

The input consists of multiple test cases. Each test case starts with a line containing M and N (2 ≤ MN ≤ 999). This line is followed by M lines containing N numbers each describing an M × N puzzle.

The input ends with a pair of zeroes which should not be processed.

Output

Output one line for each test case containing a single word YES if the puzzle can be solved and NO otherwise.

Sample Input

3 3
1 0 3
4 2 5
7 8 6
4 3
1 2 5
4 6 9
11 8 10
3 7 0
0 0

Sample Output

YES
NO


题意:给一个n*m的类似八数码的矩阵,编号为1~n*m-1,n*m转换为1,求能否从初始状态转移到给定状态。

思路:这边需要关于八数码推广的相关知识。

八数码问题的有解无解的结论:

一个状态表示成一维的形式,求出除0之外所有数字的逆序数之和,也就是每个数字前面比它大的数字的个数的和,称为这个状态的逆序。

若两个状态的逆序奇偶性相同,则可相互到达,否则不可相互到达。

由于原始状态的逆序为0(偶数),则逆序为偶数的状态有解。

也就是说,逆序的奇偶将所有的状态分为了两个等价类,同一个等价类中的状态都可相互到达。

简要说明一下:当左右移动空格时,逆序不变。当上下移动空格时,相当于将一个数字向前(或向后)移动两格,跳过的这两个数字要么都比它大(小),逆序可能±2;要么一个较大一个较小,逆序不变。所以可得结论:只要是相互可达的两个状态,它们的逆序奇偶性相同。我想半天只能说明这个结论的必要性,详细的证明请参考后面的附件。

N×N的棋盘,N为奇数时,与八数码问题相同。

N为偶数时,空格每上下移动一次,奇偶性改变。称空格位置所在的行到目标空格所在的行步数为空格的距离(不计左右距离),若两个状态的可相互到达,则有,两个状态的逆序奇偶性相同且空格距离为偶数,或者,逆序奇偶性不同且空格距离为奇数数。否则不能。

也就是说,当此表达式成立时,两个状态可相互到达:(状态1奇偶性==状态2奇偶性)==(空格距离%2==0)。

引自: http://blog.csdn.net/tiaotiaoyly/article/details/2008233

对于这题,如果列数为奇数,那么就看逆序和是否为偶数,否则需要判断逆序和的奇偶与0的初始与给定状态的行号的差值的奇偶是否相同。



万万没想到因为初始化而一直tle。。这题有毒。

#include <iostream>     
#include <string.h>    
#include <stdio.h>   
using namespace std;  
int n,m;
int cnt;
int lay[1000010];
int c[1000010];   
void update(int i, int x)//i点增量为x ,更新树状数组 
{  
    while(i <= cnt)  
    {  
        c[i] += x;  
        i += i&(-i);  
    }  
}  
int sum(int x)//区间求和 [1,x]  
{  
    int sum=0;  
    while(x>0)  
    {  
        sum+=c[x];  
        x-=x&(-x);  
    }  
    return sum;  
}  
    
int solve()
{
	int ans=0;
	for(int i=0;i<cnt;i++)
	{
		update(lay[i],1);
		ans+=i+1-sum(lay[i]);
	}
	return ans;
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		if(!n&&!m)break;
		memset(c,0,sizeof(c));
	//	memset(lay,0,sizeof(lay));   //死都没想到因为随手初始化而tle。。 
		cnt=0;
		int step=0;
		int x;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				scanf("%d",&x);
				if(x==0)step=n-1-i;
				else lay[cnt++]=x;
			}
		}
		int ans=solve();
		if(m & 1)  
            step = 0;  
        if(step % 2 == ans % 2)  
            printf("YES\n");  
        else   
            printf("NO\n");  
	}
}


Commedia dell'arte

Time Limit: 2 Seconds       Memory Limit: 65536 KB

So called commedia dell' arte is a theater genre first played at Italy in the beginning of the sixteenth century. It was inspired with the Roman Theater. The play had no fixed script and the actors (also called performers) had to improvise a lot. There were only a simple directions by the author like "enter the stage and make something funny" or "everyone comes on stage and everything is resolved happily". You can see it might be very interesting to play the commedia dell' arte. Therefore the ACM want to put a new play on a stage, which was completely unknown before. The main hero has a puzzle that takes a very important role in the play and gives an opportunity of many improvisations. The puzzle is the worldwide known Lloyd's Fifteen Puzzle. ACM wants to make the play more interesting so they want to replace the "standard" puzzle with a three-dimensional one. The puzzle consists of a cube containing M^3 slots. Each slot except one contains a cubic tile (one position is free). The tiles are numbered from 1 to M^3-1. The goal of the puzzle is to get the original ordering of the tiles after they have been randomly reshuffled. The only allowed moves are sliding a neighbouring tile into the free position along one of the three principal directions. Original configuration is when slot with coordinates (x,y,z) from {0,...,M-1}^3 contains tile number z.M^2+y.M+x+1 and slot (M-1,M-1,M-1) is free.

Your are to write a program to determine whether it is possible to solve the puzzle or not.


Input

The input consists of N cases. The first line of the input contains only positive integer N. Then follow the cases. The first line of each case contains only one integer M, 1 <= M <= 100. It is the size of 3D puzzle cube. Then follow M lines, each contains exactly M^2 numbers on the tiles for one layer. First is the layer on the top of the cube and the last one on the bottom. In each layer numbers are arranged from the left top corner linewise to the right bottom corner of the layer. In other words, slot with coordinates (x,y,z) is described by the (x+M.y+1)-th number on the (z+1)-th line. Numbers are separated by space. Number 0 means free position.


Output

For each case, print exactly one line. If the original configuration can be reached by sliding the tiles, print the sentence 'Puzzle can be solved.'. Otherwise, print the sentence 'Puzzle is unsolvable.'.


Sample Input

2
2
1 2 3 4
5 7 6 0
2
2 1 3 5
4 6 0 7


Sample Output

Puzzle is unsolvable.
Puzzle can be solved.




三维状况m^3的八数码扩展问题。

同上一题结论一样,只是从行距变成了两个维度上的距离之和。


#include <iostream>     
#include <string.h>    
#include <stdio.h>   
using namespace std;  
int m;
int cnt;
int lay[1000010];
int c[1000010];   
void update(int i, int x)//i点增量为x ,更新树状数组 
{  
    while(i <= cnt)  
    {  
        c[i] += x;  
        i += i&(-i);  
    }  
}  
int sum(int x)//区间求和 [1,x]  
{  
    int sum=0;  
    while(x>0)  
    {  
        sum+=c[x];  
        x-=x&(-x);  
    }  
    return sum;  
}  
    
int solve()
{
	int ans=0;
	for(int i=0;i<cnt;i++)
	{
		update(lay[i],1);
		ans+=i+1-sum(lay[i]);
	}
	return ans;
}
int main()
{
	int t;
	scanf("%d",&t); 
	while(t--)
	{
		scanf("%d",&m);
		memset(c,0,sizeof(c)); 
		cnt=0;
		int step=0;
		int x;
		for(int i=0;i<m;i++)
		{
			for(int k=0;k<m;k++)
			{
				for(int j=0;j<m;j++)
				{
					scanf("%d",&x);
					if(x==0)
						step=(m-1-i)+(m-1-k);
					else 
						lay[cnt++]=x;
				}
			}
		}
		int ans=solve();
		if(m & 1)  
            step = 0;  
        if(step % 2 == ans % 2)  
            printf("Puzzle can be solved.\n");  
        else   
            printf("Puzzle is unsolvable.\n");  
	}
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值