题目
众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着一种叫做苟狗的生物,这种生物天生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂 次,每次分裂后会在分裂方向前进 个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生 那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击
输入描述
输入第一行包含一个正整数 n(n <= 30),表示宇宙射线会分裂n 次
第二行包含n个正整数a1, a2···an ,第ai (ai <= 5)个数表示第 i次分裂的宇宙射线会在它原方向上继续走多少个单位长度
输出描述
输出一个数 ,表示有多少个位置会被降智打击
样例输入:
4
4 2 2 3
样例输出:
39
思路
用二维数组表示宇宙射线在二维平面上的传播。
如图
可以发现,射线在某一坐标(x, y)时,可能有8种不同的传播方向:上(①)、下(⑤)、左(⑦)、右(③)、左上(⑧)、左下(⑥)、右上(②)、右下(④)。
当射线运动到某一点进行分裂时,相应地,有如下8种可能结果:
例如,当射线分裂时的方向为①(上)时,分裂后的两个方向为⑧(左上)和②(右上)。
用数组a[31]存放射线每一次分裂后要走过的距离。
由题可知,射线最多分裂30次,每次分裂最多走5格。因此,射线朝一个方向最多走150格。于是可以定义一个平面二维矩阵为visit[400][400],用于记录射线是否经过。
笔者采用了宽度优先搜索。
如果不进行任何处理直接宽度优先搜索,则需要搜索多达230=1,073,741,824(10亿)条分裂的路径,这显然是无法接受的。
为了节约时间,可以开一个四维数组M[400][400][9][31](用空间换时间)。例如,当射线在坐标(i, j)处以方向d进行第s次分裂时,若M[i][j][d][s] = true,则说明在这之前已经有射线进行过这样的分裂了,因此无需接着往下进行搜索(剪枝);否则需要接着往下进行搜索,同时将M[i][j][d][s]置为true。
宽搜的方法就是使用一个队列,每次从队列中取出一个“路径”,将路径所经过的坐标在visit二维数组中标为true,同时这个路径还会分裂成另外两个路径,又将这两个路径放回队列中……循环至队列为空。
最后统计visit中有多少个true,即为宇宙射线经过的所有位置的总数。
代码
#include <iostream>
#include <queue>
#include <cstring>
#define MAX 400
#define DIR 8+1
#define INDEX 30+1
using namespace std;
bool visit[MAX][MAX];
bool trace[MAX][MAX][DIR][INDEX];
int n;
int a[35];
int cnt;
int minX = MAX / 2, maxX = MAX / 2;
int minY = MAX / 2, maxY = MAX / 2;
struct explodeTrack {
int x;
int y;
int dir;
int stepIndex;
explodeTrack(int theX, int theY, int theDir, int theStepIndex) : x(theX), y(theY), dir(theDir), stepIndex(theStepIndex) {}
bool operator < (const explodeTrack& t)const
{
if (x != t.x) return x < t.x;
else if (y != t.y) return y < t.y;
else if (dir != t.dir) return dir < t.dir;
else return stepIndex < t.stepIndex;
}
};
// dx[0]、dy[0]不用
int dX[9] = { 0,-1,-1, 0, 1, 1, 1, 0,-1 };
int dY[9] = { 0, 0, 1, 1, 1, 0,-1,-1,-1 };
// nextDir1[0]、nextDir2[0]不用
int nextDir1[9] = { 0,8,1,2,3,4,5,6,7 };
int nextDir2[9] = { 0,2,3,4,5,6,7,8,1 };
queue<struct explodeTrack> q;
void printBoolMatrix(bool M[MAX][MAX])
{
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
cout << M[i][j] << " ";
}
cout << endl;
}
}
void compare(int x, int y)
{
if (minX > x) minX = x;
if (maxX < x) maxX = x;
if (minY > y) minY = y;
if (maxY < y) maxY = y;
}
void track(explodeTrack& p)
{
int x = p.x;
int y = p.y;
int dir = p.dir;
int index = p.stepIndex;
int step = a[index];
int nextIndex = index + 1;
int nextDirection1 = nextDir1[dir];
int nextDirection2 = nextDir2[dir];
// 剪枝
if (trace[x][y][dir][index]) return;
trace[x][y][dir][index] = true;
int dx = dX[dir];
int dy = dY[dir];
for (int s = 0; s < step; s++) {
x += dx;
y += dy;
visit[x][y] = true;
compare(x, y);
}
if (nextIndex >= n) return;
q.push(explodeTrack(x, y, nextDirection1, nextIndex));
q.push(explodeTrack(x, y, nextDirection2, nextIndex));
return;
}
int countVisit()
{
cnt = 0;
for (int i = minX; i <= maxX; i++)
for (int j = minY; j <= maxY; j++)
if (visit[i][j]) cnt++;
return cnt;
}
void reset()
{
memset(visit, false, sizeof(visit));
memset(trace, false, sizeof(trace));
memset(a, 0, sizeof(a));
while (!q.empty()) q.pop();
}
int main()
{
#if(1)
cin >> n;
reset();
for (int i = 0; i < n; i++)
cin >> a[i];
// 起点
explodeTrack start = explodeTrack(MAX / 2, MAX / 2, 1, 0);
q.push(start);
while (!q.empty()) {
explodeTrack t = q.front();
q.pop();
track(t);
}
cout << countVisit() << endl;
#endif
return 0;
}