C 可怕的宇宙射线(Week4CSP模拟)

题目描述

众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着一种叫做苟狗的生物,这种生物天生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂 次,每次分裂后会在分裂方向前进ai个单位长度。现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生zjm那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击"。

时间内存限制

每个测试点 1000ms 262144KB

输入说明

输入第一行包含一个正整数 n(n<=30),表示宇宙射线会分裂n 次
第二行包含n个正整数a1,a2···an ,第ai 个数表示第 i次分裂的宇宙射线会在它原方向上继续走多少个单位长度

输出说明

输出一个数ans ,表示有多少个位置会被降智打击

输入样例
4
4 2 2 3
12
输出样例
39
1
测试点说明

10% n<=10
20% n<=20
30% n<=30

题目思路:

 1、这个题的整体思路是我们可以模拟射线分裂过程,当射线到达一个原来没有到达的地方时,ans++记录。
 
 2、可以利用递归实现,每一个射线会分裂出两个新的射线,其中射线步数与层数有关,射线方向与上一层射线有关。
	·射线步数可以通过递归时传参数实现,每进行一次递归,层数cnt+1
	·射线方向通过flag来判断,每一个方向对应flag数组中的下标索引1-8
	char flag[]={'up','upr','r','dor','down','dol','l','upl'};
	   
 3、   射线发射的地方开始递归,射线先前进一定距离,然后分散成两个方向,不断递归。

注意事项:

1、如果用普通深搜递归方式,函数时间复杂度是指数级的,容易tle或者mle,这道题只能拿到40%的分数。因此要利用一个四维数组剪枝
2、注意每一条射线的上下或左右端点的记录,尤其是最后一层的最后一个点,容易遗漏。
3、注意启示射线的起点位置

关于射线方向问题:

刚开始决定用递归写,发现共有八种方向的射线要讨论,与一般递归不同,这次的参数并没有非常明显的规律,例如不断加一或减一。所以刚开始我的代码递归部分是这样的:

pair<int,int> u(x,y);
	if(flag==1)//up 
	{
		boom(u,cnt+1,-1,1,4);
		boom(u,cnt+1,1,1,3);
	}
	else if(flag==2)//down
	{
		boom(u,cnt+1,1,-1,7);
		boom(u,cnt+1,-1,-1,8);
	}
	else if(flag==3)//upr
	{
		boom(u,cnt+1,0,1,1);
		boom(u,cnt+1,1,0,6);
	}
	else if(flag==4)//upl
	{
		boom(u,cnt+1,-1,0,5);
		boom(u,cnt+1,0,1,1);
	}
	else if(flag==5)//l
	{
		boom(u,cnt+1,-1,1,4);
		boom(u,cnt+1,-1,-1,8);
	}
	else if(flag==6)//r
	{
		boom(u,cnt+1,1,1,3);
		boom(u,cnt+1,1,-1,7);
	}
	else if(flag==7)//dor
	{
		boom(u,cnt+1,0,-1,2);
		boom(u,cnt+1,1,0,6);
	}
	else if(flag==8)//dol
	{
		boom(u,cnt+1,0,-1,2);
		boom(u,cnt+1,-1,0,5);
	}

有点暴力,但是确实很清晰,通过flag判断当前射线的方向,进而进行该射线分裂后两个射线的递归。
但是,代码确实有些冗余,感觉不好看。所以,我继续找射线方向之间的关系,试图用两个数组来解决(ddx,ddy)。最后根据分析可得:

char flag[]={'up','upr','r','dor','down','dol','l','upl'};//当做注释,与下面ddx,ddy对应 
const int ddx[]={0,1,1,1,0,-1,-1,-1};
const int ddy[]={1,1,0,-1,-1,-1,0,1};

例如:
upr->up和r
r->upr和dor
在上面flag数组中可以发现,每个射线分裂得到的新的两个射线方向刚好是原射线方向在数组中前后对应的方向。那么上面暴力的八个if语句就可以转换成上面两个数组形式。
(以后写代码可以先写出一个比较暴力的形式,再不断改进)

最后代码实现:

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int a[50];
map<pair<int,int>,int> mp;
bool judge[400][400][35][8]={false};
char flag[]={'up','upr','r','dor','down','dol','l','upl'};//当做注释,与下面ddx,ddy对应 
const int ddx[]={0,1,1,1,0,-1,-1,-1};
const int ddy[]={1,1,0,-1,-1,-1,0,1};
void boom(pair<int,int> p,int cnt,int dx,int dy,int flag)
{
 if(cnt==n)
 {
  return;
 }
 int x=p.first;
 int y=p.second;
 if(judge[x][y][cnt][flag])
 {
  return;
 }
 judge[x][y][cnt][flag]=true;
 for(int i=0;i<a[cnt];i++)
 {
  x+=dx;
  y+=dy;
  pair<int,int> temp(x,y);
  if(mp.count(pair<int,int>(x,y))==0)
  {
   ans++;
   mp[temp]=1;
  }
 }
 pair<int,int> u(x,y);
 boom(u,cnt+1,ddx[(flag+7)%8],ddy[(flag+7)%8],(flag+7)%8);
 boom(u,cnt+1,ddx[(flag+1)%8],ddy[(flag+1)%8],(flag+1)%8);
}
int main()
{
 cin>>n;
 for(int i=0;i<n;i++)
 {
  cin>>a[i];
 }
 pair<int,int> p(200,200);
 boom(p,0,0,1,0);
 cout<<ans<<endl;
 return 0;
}
发布了24 篇原创文章 · 获赞 9 · 访问量 7172

猜你喜欢

转载自blog.csdn.net/qq_40103496/article/details/104840157