题目描述:
宇宙射线会在无限的二维平面上传播(可看作一个二维网格图),初始方向默认向上。宇宙射线在发射出一段距离后分裂,向该方向左右45。分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后前进ai个单位长度。
输入描述:
输入第一行包含一个正整数n(n<=30)表示宇宙射线会分裂n次
第二行包含n个整数,a1,`a2,…,an,第i个数ai(ai<=5)表示第i次分裂的宇宙射线会在原方向走多少单位长度。
样例输入:
4
4 2 2 3
输出描述:
输出一个数ans,表示宇宙射线走过的单位数。
样例输出:
39
思路:
该题的难点在与要对射线分裂时路径上重复点的去除。
在这里建立了一个bool的二维数组note来标记该点是否走过,用于去重。因为ai<=5,n<=30,故数组大小300300已经够用,在这里取400400。
模拟宇宙射线在二维平面上的行进路线可以使用bfs,也可以使用dfs,在这里使用了bfs的方法。将宇宙射线的每一次分裂,看作是bfs向外的一次扩展。在这里,可建立一个三维数组,各维含义分别为x坐标,y坐标,方向,用来标记下一层扩展的点,如果这一点在数组中已经被标记过,则说明这一条路径在之前bfs的过程中已经走过,可以直接舍弃此条路径。如此,可以大大降低时间复杂度。
代码:
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
struct point
{
int x;
int y;
int length;
point(int thex, int they)
{
x = thex;
y = they;
length = 0;
}
point(int thex, int they,int thelength)
{
x = thex;
y = they;
length = thelength;
}
};
int dx[] = { 0,-1,-1,-1,0,1,1,1 };
int dy[] = { 1,1,0,-1,-1,-1,0,1 };
bool vis[400][400];
bool note[400][400][8];
queue<point>q;
int main()
{
int n;
cin >> n;
point now(199, 199);
int *go = new int[n]();
for (int i = 0; i < n; i++)
cin >> go[i];
int count = 0, number = 1;
q.push(now);
memset(vis, false, 400 * 400 * sizeof(bool));
for (int i = 0; i < n; i++)
{
memset(note, false, 400 * 400 * 8 * sizeof(bool));
int temp = 0;
for (int j = 0; j < number; j++)
{
now = q.front();
int before = q.front().length;
q.pop();
for (int k = 0; k < go[i]; k++)
{
now.x += dx[before];
now.y += dy[before];
if (!vis[now.x][now.y])
{
vis[now.x][now.y] = true;
count++;
}
}
if (!note[now.x][now.y][(before + 1) % 8])
{
note[now.x][now.y][(before + 1) % 8] = true;
now.length = (before + 1) % 8;
q.push(now);
temp++;
}
if (!note[now.x][now.y][(before + 7) % 8])
{
note[now.x][now.y][(before + 7) % 8] = true;
now.length = (before + 7) % 8;
q.push(now);
temp++;
}
}
number = temp;
}
cout << count << endl;
return 0;
}