可怕的宇宙射线

题意:

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

时间与内存限制 1000ms 2632144KB

输入输出说明:

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

数据点 n
10% <=10
40% <=20
100% <=30

样例输入:

4
4 2 2 3

样例输出:

39

样例说明
在这里插入图片描述

思路:

算法确定

这个题目要求计算出共有多少个位置会被"降智打击",与坐标有关,让我想起了之前讲过的“迷宫问题”,到这里我觉得可能使用bfs或dfs。在这个宇宙射线的分裂过程中,它的两边是对称的,除了方向不同之外,其余都是一样的,所以我先考虑分裂的一半,分裂结束之后,回溯到上一个分裂点,加上分裂的另一半,考虑到这,基本上可以确定使用dfs遍历了。

具体实现:

如何确定经过的点的个数?参照dfs遍历中使用vis数组进行标记,这里需要注意的是,之前做的题都是向4个方向移动,而这个题是45°角分裂,也就是有8个方向,我们还是使用常量数组dx,dy来控制移动。

上面我们已经分析过了,每次都先考虑分裂的一半,需要注意的一点是,我们跟踪哪一半的分裂就一直跟踪这一半,我在这道题中先考虑每次分裂后的右枝,所以dx,dy的定义如下:

int dx[8]={0,1,1,1,0,-1,-1,-1};
int dy[8]={1,1,0,-1,-1,-1,0,1};

剪枝:

一个点若在之前已经进行过某个方向的某种分裂,那么它之后经过的路径与上一次经过这个分裂点时的历经是一致的,也就是说它必定已经被标记过,若继续进行下去就是在做无用功,所以这里有了优化条件:设置数组divide记录是否进行过相同的分裂,若进行过,则停止遍历。

总结:

1.由分裂的对称性想到先考虑分裂的一枝
2.暴力的使用bfs,dfs遍历一般都复杂性较高,剪枝对于降低复杂性十分必要
3.八个方向合理使用dx,dy数组,保证每次跟踪的每一枝是固定的(左枝or右枝)
4.再就是注意细节!卡了好长时间的一个错误竟然是少写了逗号

代码:

#include<iostream>
using namespace std;

int dx[8]={0,1,1,1,0,-1,-1,-1};
int dy[8]={1,1,0,-1,-1,-1,0,1}; 	//方向为右45°,顺时针 
int vis[301][301];	//记录该点是否到达过
int divide[301][301][31][8];  //记录该点是否分裂过
int n,res;
int a[31];

void dfs(int x,int y,int count,int direction){
	if(count>n||divide[x][y][count][direction]==1) //输入结束或该点分裂过 
		return;   
	divide[x][y][count][direction]=1;			//标记分裂过 
	int X,Y; 
	for(int i=1;i<=a[count];i++){
		X=x+dx[direction]*i;Y=y+dy[direction]*i;
		if(vis[X][Y]==0){
			vis[X][Y]=1; res++;			//标记到达过 
		} 	
	}
	count++;
	int Direction=(direction+1)%8;
	dfs(X,Y,count,Direction);		//右枝
	Direction=(direction+7)%8;
	dfs(X,Y,count,Direction); 	//左枝  
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	dfs(150,150,1,0);
	cout<<res<<endl;
	return 0;
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值