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,这样一来一回有可能保持原样。所应该用隐式图遍历,因为这里的不能让相同的状态重复执行,并且要求的是最少次数,隐式图是bfs遍历,正好是先找到终态即路径最短。这里对于一个状态需要从头开始每个数字都考虑一下,能否和其他的数字跳舞,邀请成功后就要选择站到左边还是右边。

注意:插入到某个数的左边,右边情况还需要根据跳舞的两个数的位置来定,要细心。

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;

const int MAXN = 1000005;
const int N = 8;
int state[MAXN][N], head[MAXN], next[MAXN], dist[MAXN];

bool cmp (const int& a, const int& b) {

	if (abs(a) < abs(b))
		return true;
	return false;
}

bool is_prime (int n) {

	for (int i = 2; i <= sqrt(n); i++)
		if (n % i == 0)
			return false;
	return true;
}

int hash (int rear) {

	int sum = 0;
	for (int i = 0; i < N; i++)
		sum = sum  * 10 + abs(state[rear][i]);
	return sum % MAXN;
}

bool trytoinsert (int rear) {

	int p = hash (rear);
	int u = head[p];
	while (u) {

		if (memcmp(state[rear], state[u], sizeof (state[u])) == 0)
			return false;
		u = next[u];
	}

	next[rear] = head[p];
	head[p] = rear;
	return true;
}
//插入到某个数的左边或是右边 p1要插入的数的位置, p2被插队的那个数的位置,dir = 0插入到左边,dir = 1插入到右边
void change (int p1, int p2, int front, int& rear, int dir) {

	int temp = state[front][p1];
	memcpy (state[rear], state[front], sizeof (state[front]));
	if (p1 < p2) {

		if (dir == 0) {

			for (int i = p1 + 1; i < p2; i++)
				state[rear][i - 1] = state[rear][i];
			state[rear][p2 - 1] = temp;
		} else {

			for (int i = p1 + 1;i <= p2; i++)
				state[rear][i - 1] = state[rear][i];
			state[rear][p2] = temp;

		}
	}
	if (p1 > p2) {

		if (dir == 1) {

			for (int i = p1; i > p2; i--)
				state[rear][i] = state[rear][i - 1];
			state[rear][p2 + 1] = temp;
		} else {

			for (int i = p1; i >= p2; i--)
				state[rear][i]= state[rear][i - 1];
			state[rear][p2] = temp;
		}
	}//判断该状态是否重复,不重复就入队
	if (trytoinsert(rear)) {

		dist[rear] = dist[front] + 1;
		rear++;
	}
}

int bfs () {

	int front, rear;
	front = 1;
	rear = 2;
	memset (dist, 0, sizeof (dist));
	memset (head, 0, sizeof(head));
	while (front < rear) {

		if (memcmp (state[0], state[front], sizeof (state[0])) == 0)
			return dist[front];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {

				if (i != j && state[front][i] * state[front][j] < 0 && is_prime (abs (state[front][i]) + abs (state[front][j]))) {

					change (i, j, front, rear, 0);
					change (i, j, front, rear, 1);
				}
			}
		}
		front++;
	}
	return -1;
}

int main () {

	int t = 0;
	while (scanf ("%d", &state[1][0]), state[1][0]) {

		for (int i = 1; i < N; i++)
			scanf ("%d", &state[1][i]);
		memcpy(state[0], state[1], sizeof (state[1]));
		sort (state[0], state[0] + N, cmp);
		printf ("Case %d: %d\n", ++t, bfs());
	}
	return 0;
}

11198 - Dancing Digits(BFS + hash判重)

时间: 2024-08-10 23:59:17

11198 - Dancing Digits(BFS + hash判重)的相关文章

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,

HDU ACM 1067 Gap-&gt;BFS+HASH判重

题意:初始状态为左边空一行,数字在右边28个格子.末态要按一副卡片在一行顺序牌,即第一行为11-17,第二行21-27,....,可以通过四个空格来转移卡片,问从初始状态到末态最少要多少步. 分析: 1.每次操作只能把一个数字放到某个空格,不能交换两个数字的位置. 2.用的是再哈希法hash =(v+10)%M来处理冲突. 3.空格的左边为空或者数字的末尾为7则不能填充. 4.填充空格要找比他左边大1的数来填充. #include<iostream> #include<queue>

codevs 1004 四子连棋 BFS、hash判重

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

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的结果进行判重,只保留相同余数的最小数

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

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

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

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

【BZOJ】1054: [HAOI2008]移动玩具(bfs+hash)

http://www.lydsy.com/JudgeOnline/problem.php?id=1054 一开始我还以为要双向广搜....但是很水的数据,不需要了. 直接bfs+hash判重即可. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> using n