HDU ACM 1067 Gap->BFS+HASH判重

题意:初始状态为左边空一行,数字在右边28个格子。末态要按一副卡片在一行顺序牌,即第一行为11-17,第二行21-27,。。。。,可以通过四个空格来转移卡片,问从初始状态到末态最少要多少步。

分析:

1、每次操作只能把一个数字放到某个空格,不能交换两个数字的位置。

2、用的是再哈希法hash =(v+10)%M来处理冲突。

3、空格的左边为空或者数字的末尾为7则不能填充。

4、填充空格要找比他左边大1的数来填充。

#include<iostream>
#include<queue>
using namespace std;

#define MOD 1000007
__int64 Hash[1000007];   //hash表,用于hash判重

struct Node
{
	int map[4][8];    //图
	int step;         //步数
	bool operator==(const Node& p)const  //判断相等
	{
		int i,j;

		for(i=0;i<4;i++)
			for(j=0;j<8;j++)
				if(map[i][j]!=p.map[i][j])
					return false;
		return true;
	}
	__int64 HashValue()  //hash值
	{
		__int64 v=0;
		int i,j;

		for(i=0;i<4;i++)
			for(j=0;j<8;j++)
				v+=(v<<1)+map[i][j];
		return v;
	}
};

Node s,e;    //初始节点和结束节点

void Swap(int& a,int& b)
{
	int tmp;

	tmp=a;
	a=b;
	b=tmp;
}

void Read()
{
	int i,j;

	memset(Hash,-1,sizeof(Hash));
	for(i=0;i<4;i++)               //初始第0列为空
	{
		s.map[i][0]=0;
		for(j=1;j<8;j++)
			scanf("%d",&s.map[i][j]);
	}
	s.step=0;
}

void GetEnd()    //获得终态
{
	int i,j;

	for(i=0;i<4;i++)
	{
		e.map[i][7]=0;
		for(j=0;j<7;j++)
			e.map[i][j]=(i+1)*10+(j+1);
	}
}

bool HashJudge(__int64 value)  //hash判重
{
	int v;

	v=value%MOD;
	while(Hash[v]!=-1 && Hash[v]!=value)  //冲突处理,在hash法
	{
		v+=10;
		v%=MOD;
	}
	if(Hash[v]==-1)
	{
		Hash[v]=value;
		return true;
	}
	return false;
}

bool bfs(int& ans)
{
	queue<Node> q;
	Node p,p2;
	int i,j,x,y,value,k,l;
	bool fg;
	__int64 ha;

	q.push(s);                 //初始节点入队
	HashJudge(s.HashValue());
	while(!q.empty())
	{
		p=q.front();
		q.pop();
		for(i=0;i<4;i++)
			for(j=0;j<8;j++)
			{
				if(!p.map[i][j])  //格子为空
				{
					p2=p;
					p2.step++;
					value=p.map[i][j-1]+1;  //找比map[i][j-1]大1的数
					if(value==1||value%10==8) //0或者value为7的不能移动
						continue;
					fg=true;
					for(k=0;k<4&&fg;k++)
						for(l=1;l<8&&fg;l++)
							if(p.map[k][l]==value)
							{
								x=k;
								y=l;
								fg=false;
							}
					if(!fg)
					{
						Swap(p2.map[i][j],p2.map[x][y]);
						ha=p2.HashValue();
						if(HashJudge(ha))   //hash判重
						{
							if(p2==e)
							{
								ans=p2.step;
								return true;
							}
							q.push(p2);
						}
					}
				}
			}
	}
	return false;
}

void Process()
{
	int k,i,j,ans;

	k=0;
	for(i=0;i<4;i++)   //初始时将11,21,31,41移动到第0列
		for(j=1;j<8;j++)
			if(s.map[i][j]==(k+1)*10+1)
			{
				Swap(s.map[i][j],s.map[k][0]);
				k++,i=0,j=0;
			}
	if(s==e)
	{
		cout<<0<<endl;  //前四步不记录
		return ;
	}
	if(bfs(ans))
		cout<<ans<<endl;
	else
		cout<<-1<<endl;
}

int main()
{
	int T;

	scanf("%d",&T);
	GetEnd();
	while(T--)
	{
		Read();
		Process();
	}
    return 0;
}
时间: 2024-10-24 01:41:08

HDU ACM 1067 Gap->BFS+HASH判重的相关文章

11198 - Dancing Digits(BFS + hash判重)

题目:11198 - Dancing Digits 题目大意:每组数据给出8个数字,可能正可能负.要求最后将这8个数字按照数字绝对值从小到大的排序.排序的规则是让某个数字a邀请另一个数字b跳舞,这样a就可以插到b的左边或是右边,a能邀请b跳舞,则a* b <0 ,且a+b要是素数.题目问给出一组数据问能否通过邀请跳舞来排序,能的话就输出最少的邀请次数,否则输出-1. 解题思路:这题一开始竟然想着dfs,但是后面发现,这样的判断树可以是无限大,因为可以a邀请完b,然后b在邀请a,这样一来一回有可能

HDU 1067 Gap BFS+Hash

感觉可以用bfs暴力,一开始写了一个很暴力的bfs,hash判重,本地跑了几组数据发现至少要2s才能过,交上去估计要TLE,试探性的交了一发1A了,原来时限是10s,我看成1s了,真是逗逼...还一直不敢写 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include &

HDU 4474&amp;&amp;POJ 1465 BFS&amp;&amp;余数判重

两道题只是输入输出格式略有不同 给出n,m,m个数 要求生成一个数x,是n的倍数,并且只由这M个数构成(或不能含有这M个数中的任意一个),求最小的x,没有则输出-1(0): BFS找每一个满足n的倍数的数,用余数判重优化 对于给定两个数A,B,如果A和B模N都相同,设为C,即: A=x*N+C B=y*N+C 那么如果在A后面加上一个数字能够被N整除,则在B后面加上这个数字也肯定可以被N整除 即:(A*10+X)%N==(B*10+X)%N 因此可以利用模N的结果进行判重,只保留相同余数的最小数

HDU 1067 HASH判重BFS

给出起始状态如: 问最少多少步操作可以变为: 每次操作只能把一个数字放到某个空格,不能交换两个数字的位置 hash判重模板mark一个 #include "stdio.h" #include "string.h" #include "queue" using namespace std; const int mod=1000007; int aim[4][8]= { {11,12,13,14,15,16,17}, {21,22,23,24,25,

codevs 1004 四子连棋 BFS、hash判重

004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input

[bfs+余数判重+路径记录] hdu 4474 Yet Another Multiple Problem

题意: 给一个n和m个数字(一位数) 求最小的n的倍数不含有这m个数字,不存在输出-1 思路: 首先有可能这个数超long long 所以无法暴力解决 所以这题应该是一个bfs 为什么能用余数判重呢 对于当前的余数进到队列里,一定是这个余数对应数的最小值 接下来再怎么添加到满足条件的后续东西应该是一样的 所以就可以余数判重了,类似数位dp的记录方式 然后再加上一个路径记录就好了 代码: #include"cstdlib" #include"cstdio" #incl

poj1465Multiple(经典BFS+余数判重)

Multiple Time Limit: 1000MS   Memory Limit: 32768K Total Submissions: 6936   Accepted: 1495 Description a program that, given a natural number N between 0 and 4999 (inclusively), and M distinct decimal digits X1,X2..XM (at least one), finds the small

Keyboarding (bfs+预处理+判重优化)

# #10030. 「一本通 1.4 练习 2」Keyboarding [题目描述] 给定一个 $r$ 行 $c$ 列的在电视上的"虚拟键盘",通过「上,下,左,右,选择」共 $5$ 个控制键,你可以移动电视屏幕上的光标来打印文本.一开始,光标在键盘的左上角,每次按方向键,光标总是跳到下一个在该方向上与当前位置不同的字符,若不存在则不移动.每次按选择键,则将光标所在位置的字符打印出来. 现在求打印给定文本(要在结尾打印换行符)的最少按键次数. [算法] 1.预处理四个方向能到达的点.

HDU 2579 Dating with girls(2) BFS 余数判重

对于石头的处理就按照每个位置的时间取k的余数判一下重复就好,其他随意写 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queue> #include <deque> #include