首页 > 代码库 > 06day2

06day2

蠕虫游戏

模拟

【问题描述】

蠕虫是一个古老的电脑游戏,它有许多版本。但所有版本都有一个共同规则:操纵一条蠕虫在屏幕上转圈,并试着去避免撞到自己或障碍物。

这里我们将模拟一个简单的版本。游戏将在 50×50 的棋盘上进行,棋盘的左上角为(1,1),蠕虫在初始时是一串 20 个相连的方格。所谓连接是指方格在水平或垂直方向上相接。蠕虫开始时是水平地伸展开的,从(25,11)到(25,30)。其中(25,30)是它的头。蠕虫只能向东(E),西(W),南(S),北(N)四个方向移动,但不能向自己移动,因此在开始时向西(W)是不允许的。每次移动时,蠕虫向给定的方向移动,一次只移动一格,并且保证它的长度不变。因此只有蠕虫的头和尾所占据的方格在移动一步后被改变。注意:蠕虫的头能移动到虫尾刚刚让出的空格。

你将被给定一系列移动指令并模拟虫的移动,直到:蠕虫撞上了自己;蠕虫越出了棋盘;蠕虫成功地完成了这些指令。在前两种情况下你应当忽略剩下的指令。

【输入】

每个输入文件包含了很多组数据。每个数据占 2 行,第一行是一个整数(n<100),表示移动指令的指令数(以 n=0 表示输入结束);第 2 行包括了 n 个字符(E,W,S,N),字符之间没有空格,表示移动的指令。

【输出】

每个数据输出一行,格式为以下 3 种中的一种(m 是你要决定输出的步数):

The worm ran into itself on move m.

The worm ran off the board on move m.

The worm successfully made all m moves.

【解题过程】

我用了一个数组来记录 20 个点的坐标。贪吃蛇移动一步可以当做是尾部的点消失并出现在头部,这样处理是最方便的。

初次提交 AC。

?

直角三角形

离散化

【问题描述】

平面上给定 n 个两两不同的整数点,统计以给定的点为顶点,其直角边平行于坐标轴的直角三角形的个数。

【输入】

输入文件第一行是一个整数 n。

以下 n 行,每行是一个点的坐标。

【输出】

输出一个整数,表示统计结果。

【输入样例】

4

0 0

0 1

1 0

1 1

【输出样例】

4

【数据规模】

30%的数据满足 n≤100;

50%的数据满足 n≤1000;

100%的数据满足 0<n≤100,000,所有坐标不超过 32 位整数范围。

【解题过程】

看到 10^6 就又想到排序了。

思路很明显:枚举直角顶点是哪一个,则能构成的直角三角形的数目=横坐标与它相同的点的数目*纵坐标与它相同的点的数目。

如何统计横纵坐标相同的点的数目?先按横坐标排序,横坐标相同的点必然排在一起,统计并记录到数组中。比如对于排序后第 7 个点有 3 个点的横坐标与其相同,就令 x[7] = 3. 再按纵坐标排序,然后计算。

初次提交 AC。

?

聪明的打字员

搜索

【问题描述】

阿兰是某机密部门的打字员,出于保密的需要,该部门用于输入密码的键盘是特殊设计的,键盘上没有数字键,而只有以下六个键:Swap0, Swap1, Up, Down, Left, Right。为了说明这六个键的作用,我们先定义录入区的 6 个位置的编号,从左至右依次为 1,2,3,4,5,6。下面列出每个键的作用:

Swap0:按 Swap0,光标位置不变,将光标所在位置的数字与录入区的 1 号位置的数字(左起第一个数字)交换。如果光标已经处在录入区的 1 号位置,则按 Swap0 键之后,录入区的数字不变;

Swap1:按 Swap1,光标位置不变,将光标所在位置的数字与录入区的 6 号位置的数字(左起第六个数字)交换。如果光标已经处在录入区的 6 号位置,则按 Swap1 键之后,录入区的数字不变;

Up:按 Up,光标位置不变,将光标所在位置的数字加 1(除非该数字是 9)。例如,如果光标所在位置的数字为 2,按 Up 之后,该处的数字变为 3;如果该处数字为 9,则按 Up 之后,数字不变,光标位置也不变;

Down:按 Down,光标位置不变,将光标所在位置的数字减 1(除非该数字是 0),如果该处数字为 0,则按 Down 之后,数字不变,光标位置也不变;

Left:按 Left,光标左移一个位置,如果光标已经在录入区的 1 号位置(左起第一个位置)上,则光标不动;

Right:按 Right,光标右移一个位置,如果光标已经在录入区的 6 号位置(左起第六个位置)上,则光标不动。

当然,为了使这样的键盘发挥作用,每次录入密码之前,录入区总会随机出现一个长度为 6 的初始密码,而且光标固定出现在 1 号位置上。当巧妙地使用上述六个特殊键之后,可以得到目标密码,这时光标允许停在任何一个位置。

现在,阿兰有一个 6 位的数字密码,请编写一个程序,求出录入一个密码需要的最少的击键次数。

【输入】

仅一行,含有两个长度为 6 的数,前者为初始密码,后者为目标密码,两数间用空格隔开。

【输出】

仅一行,含有一个正整数,为最少需要的击键次数。

【输入样例】

123456 654321

【输出样例】

11

【解题过程】

丧心病狂的搜索题。

感觉 BFS 不太够,就想 A*。但是 h(x) 函数不好设计,如果 h(x) = 位置正确的数字个数,那么效果不好,对搜索没有太大的促进作用(甚至会因为优先队列的复杂度较大而劣于直接 BFS);如果 h(x) = 每个位置上的数字与目标数字的差的绝对值之和,其实是错的,因为有 Swap 操作的存在使得可以一步让两个不同的数字到正确的位置,这样不满足 h(x)<=h*(x) 的要求。

那还是直接 BFS 吧。但对于极限数据 000000 999999 基本上没希望(5s+)。

第一次提交 70 分。

后来看题解看到一个剪枝:

  • 如果当前数字已经等于目标状态中所有数字的最大值,就不执行 Up 操作;如果当前数字已经等于目标状态中所有数字的最小值,就不执行 Down 操作

后来想想这个剪枝是显而易见的,只能说做题的时候太浮躁了。

另外还要注意一下常数的优化。做这道题一般是把六个数字加上位置信息压成 7 位十进制数,而扩展状态的时候对于有些操作不必将 7 位数展开到数组里再操作,比如 Up 操作完全可以通过加上 10 的幂来改变某一位上的数字,这样会比拆分成数字再拼合成整数要快不少。

加了如上优化就能 AC 老师给的数据。

06day2