宇宙射線會在無線的二維平面上傳播(可看成一個二維網格圖),初始方向默認向上,宇宙射線會在發射出一段距離後進行分裂,向着該方向的左右45°方向分裂出兩條宇宙射線,同時威力不變。宇宙射線會分裂n次,每次分裂後會在分裂方向前進ai個單位長度。請計算出共有多少個位置會被打擊到。
輸入第一行一個正整數n(n<=30),表示宇宙射線會分裂n次。第二行包含n個整數a1,a2…an,第i個數字ai(ai<=5)表示第i次分裂的宇宙射線會在原方向上再繼續走多少個單位長度。
輸出一個數ans,表示會有多少個位置被打擊到。
sample input:
4
4 2 2 3
sample output:
39
圖示:
思路:
- 對於所有的點,只可能有八種前進方向。並且每次只可能是逆時針和順時針的45度,所以可以按照順時針或者逆時針的方向對應寫出x,y座標的變化,這樣只需要記錄下每一次的轉變方向,就可以通過%8+1和-1的方式得到這次的轉向。
- 可以利用一個bool型二維數組hit[x][y]來表示這個座標(x,y)是否被訪問到,最後計算hit的點的數量即可得到答案
- 利用廣搜的方式,對於每一個分支節點進行兩個遞歸繼續搜索
- 如果直接按照上面的思路的話,可以過5個點,其餘的點會TLE,爲了降低時間複雜度,我們需要對現在的程序進行剪枝
- 程序消耗的時間過多的主要原因是,當點的數量較大的時候,可能會出現在進行某一層時,同時有幾個分支到達了同一個點,那麼對於同樣的點,不同的分支都會將他走到最後去,導致了很多的冗餘。所以創建一個四維數組,用來表示在某一層的某一個點上的某個方向,如果同樣的點出現,就直接跳出。
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int dirx[8]={0,-1,-1,-1,0,1,1,1};
int diry[8]={1,1,0,-1,-1,-1,0,1};//一圈方便循環
int ans=0;
int t[31];
int n;
bool hit[400][400]={false};
bool visit[31][400][400][8];
void digui(int now,int x,int y,int d)
{
if(visit[now][x][y][d]==true)//降低複雜度
return;
visit[now][x][y][d]=true;
if(!hit[x][y])
{
hit[x][y]=true;
ans++;
}
int temp_x=x,temp_y=y;
for(int i=0;i<t[now]-1;i++)
{
temp_x+=dirx[d];
temp_y+=diry[d];
if(!hit[temp_x][temp_y])
{
hit[temp_x][temp_y]=true;
ans++;
}
}
if(now==n) return;//返回條件
int temp_d=(d+1)%8;//45
digui(now+1,temp_x+dirx[temp_d],temp_y+diry[temp_d],temp_d);
temp_d=(d+7)%8;
digui(now+1,temp_x+dirx[temp_d],temp_y+diry[temp_d],temp_d);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>t[i];
digui(1,200,200,0);//第一個的方向肯定是向上
cout<<ans;
return 0;
}