CSP模测——可怕的宇宙射线

可怕的宇宙射线

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

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

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

四、样例输入输出

Input

4
4 2 2 3

Output

39

五、数据范围

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

六、样例图示
在这里插入图片描述

七、解题思路
将宇宙射线的每一次分裂视为一层,使用dfs算法进行逐层的查找,标记。
因为至多分裂30次,每次最多移动5,所以可以开一个400×400的数组存放地图。在每层的查找中,观察发现,上一层的射线可以由分裂下一层的射线转动九十度得到,所以在开始的查找中,可以只标记向右45°的分裂,之后从最后一层开始递归往前一层,每一层先标记该次向右45°分裂的点,然后对该层所有标记的点转动九十度得到的点进行标点,直至查找到第一层。

总结:该题作为csp模测第三题,对应csp第四题,相对难度较高。在开始写的时候首先要注意数据范围和时间空间要求,考虑自己写的算法的复杂度是否过高。同时要注意部分分分重要性,可以一部分一部分的分得。我在开始写的时候,按照迷宫问题的思路,bfs算法加堆栈存储每个分裂尾点,进行标点,之后发现分裂30次后,相当于要对2^30的数据做处理,时间复杂度,空间复杂度都是超的,模测时也没能写完。最后写完加上剪枝优化也只能得40分,算法指数爆炸不可取,该代码也会作为错误样例在样例中展示。
注意事项:数组不要开的刚刚好,可能有考虑不到的情况,容易出现WA,亲身体验,所以后来开一个400×400的。

八、样例代码

正确样例(100分)

#include<iostream>
using namespace std;
int n; 
int *a;
struct point
{
    int x,y;
	point()
	{
		x=0;
		y=0;	
	} 
	point(int xx,int yy)
	{
		x=xx;
		y=yy;
    }
};

int d[8][2]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; //北开始的顺时针 
bool map[400][400]={false};
int m[400][400]; 

void turn(point p,int f,point &pp,int xx,int yy)  //翻转对称 
{
	f=f%4;
	switch(f)
	{
		case 0:
			pp.x=p.x+p.y-yy;  
	    	pp.y=p.x+p.y-xx;
	    	break;			
	    case 1:
		    pp.x=2*p.x-xx;
	        pp.y=yy;
	        break;	    		
	    case 2:
	    	pp.x=yy-p.y+p.x;
	    	pp.y=xx+p.y-p.x;
	    	break;
	    case 3:
     		pp.x=xx;
	    	pp.y=2*p.y-yy;
	    	break;
	}
}

void find(int i,int n,int f,point &p)
{
	if(i>=n) return;  //大于n,返回 
	
	int x=p.x;
	int y=p.y;
	x=x+d[f][0]*a[i];
	y=y+d[f][1]*a[i];
	point p1(x,y);       //终点 
	int rf=(f+1)%8; 
	find(i+1,n,rf,p1); //递归 
	x=p.x,y=p.y; 
//	cout<<i<<endl;     
	for(int j=0;j<a[i];j++)
	{
		x=x+d[f][0];
		y=y+d[f][1];
		map[x][y]=true;
	}  

	if(i!=0)
	{
		for(int xx=0;xx<400;xx++)
		{
	        for(int yy=0;yy<400;yy++) 
	        {
	            if(map[xx][yy]==true)
        		{
                    point pp;
                 //   cout<<f<<endl;
		         	turn(p,f,pp,xx,yy);
	 	            map[pp.x][pp.y]=true;  	 	
	            }
			}
		}
	}
}

int main()
{
	cin>>n;
	a = new int[n];
	for(int i=0;i<n;i++) cin>>a[i];  //输入
	
	for(int i=0;i<400;i++)
	    for(int j=0;j<400;j++)
	        m[i][j]=-1;
	

    point begin(200,200);
	find(0,n,0,begin);
	
	int sum=0;
	for(int xx=0;xx<400;xx++)
	   for(int yy=0;yy<400;yy++)
	   {
	   	if(map[xx][yy]) sum++;
	   }
	cout<<sum<<endl;
	return 0;
}

错误样例(40分)

#include<iostream>
#include<queue>
#include<math.h>

using namespace std;
int n;
bool map[300][300]={false};
int d[8][2]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; //北开始的顺时针 

struct point
{
    int x,y;
	int z; 
	point(int xx,int yy,int zz)
	{
		x=xx;y=yy;	
		z=zz;
	}
};

struct Direction
{
	int f[8];//记录一个点向各个方向移动的距离 
	dic()
	{
		for(int i=0;i<8;i++) f[i]=0;
	}
};

Direction dir[300][300];
queue<point> Q;
int ans=0;

int main()
{
	cin>>n;
    int a[n]={0};
	for(int i=0;i<n;i++) cin>>a[i];
	Q.push(point(150,150,0));//将起始点加入
	for(int i=0;i<n;i++)
	{
        bool judge=false; 
		for(int j=0;j<pow(2,i);j++)                //算法复杂度过高 
     	{
     		point p=Q.front();
    		Q.pop(); 
		//	for(int w=0;w<8;w++)
	    //      cout<<p.f[w]<<endl;
    		int _x=p.x;int _y=p.y;int _z=p.z;
    		if(dir[_x][_y].f[_z]>=a[i])//当这个点往这个方向走的多于这个的时候,跳出
			{
				Q.push(point(_x+d[_z][0]*a[i],_y+d[_z][1]*a[i],(_z+1+8)%8));
                Q.push(point(_x+d[_z][0]*a[i],_y+d[_z][1]*a[i],(_z-1+8)%8));
			} 
    		else
			{
			    judge=true;
    	        dir[_x][_y].f[_z]=a[i];
    	    	for(int k=0;k<a[i];k++)
    	    	{   
		     	  // 	cout<<_x-150<<" "<<_y-150<<endl; 
	    		    _x=_x+d[_z][0];
	     		    _y=_y+d[_z][1];
	     		    dir[_x][_y].f[_z]=a[i]-k-1;
    	    		map[_x][_y]=true;
    	    	///	cout<<_x-150<<" "<<_y-150<<"!!"<<k<<" "<<_z<<endl;
		    	}
                Q.push(point(_x,_y,(_z+1+8)%8));
                Q.push(point(_x,_y,(_z-1+8)%8));
            } 
		}
		if(!judge) break;
	}
	for(int i=0;i<300;i++)
	    for(int j=0;j<300;j++)
		{
			if(map[i][j]==true)
			ans++;
		}
	cout<<ans<<endl; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值