题目描述
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变。宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai 个单位长度。计算出共有多少个位置会被打击。
Input
输入第一行包含一个正整数n(n <= 30),表示宇宙射线会分裂n次,第二行包含n个正整数a1,a2…an,第i个数ai(ai <= 5)表示第i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
Output
输出一个数ans,表示有多少个位置会被降智打击。
Sample Input
4
4 2 2 3
Sample Output
39
解题思路
本题思路容易想到,通过bfs或者dfs直接搜索。但在这之前,要先用一个二维数组存储该网格中的每个点是否到达。由于刚开始使用的是bfs,但最后总是报错(对不起,是我太菜),后来就使用了dfs,并用记忆化搜素记录搜索过的路径,来进行“剪枝”操作,降低时间复杂度。
除此之外,此题中一个难以解决的是方向问题,但经过仔细分析后,发现八个方向是有规律的,用一个数组有规律的存储这八个方向,则可以发现如果将方向数组看成环,那么每个方向结束后的下面两个方向都是环中前后各一个方向,这样就确定了下一次的偏移。
代码
#include<iostream>
#include<string>
#include<queue>
#include<cmath>
using namespace std;
//int graph[300][300];
bool arr[300][300][30][8]={false};
bool arr2[300][300]={false};
struct node
{
int x,y;
};
int ans=0;
node dir[8] = {{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};//按照左下,左,左上,上,右上,右,右下,下的顺序排列
void search(node start,int d,node dir2,int m,int *a,int n)
{
if(m==n)
{
return ;
}
if(arr[start.x][start.y][m][d]==true)//如果和之前的轨迹完全重合,则可以直接跳过
{
return;
}
arr[start.x][start.y][m][d]=true;//存在这条路径
// ans++;
for(int i=0;i<a[m];i++)
{
start.x+=dir2.x;
start.y+=dir2.y;
if(arr2[start.x][start.y]!=true)
{
arr2[start.x][start.y]=true;
ans++;
}
}
search(start,(d+1)%8,dir[(d+1)%8],m+1,a,n);//右边的方向
search(start,(d+7)%8,dir[(d+7)%8],m+1,a,n);//左边的方向
}
int main()
{
int n;
cin>>n;
int a[30];
for(int i=0;i<n;i++)
cin>>a[i];
node start={150,150};//起点
node dir2=dir[3];//第一次的方向
// int m=0;//递归次数
search(start,3,dir2,0,a,n);//四个参数分别是每次递归的起点,每一次移动的个数,第几次递归,总递归次数,每次递归的起点
cout<<ans<<endl;
return 0;
}
反思
本题中的记忆化搜索在之前是没有关注过的,或者说没发现其妙处。但在此题中,刚开始并没有使用记忆化搜索时,发现会超时。但在利用记忆化搜索进行“剪枝”后,时间立马降了下来。后来想起来迷宫问题也用过类似的操作。以后可以常用!