首页 » 技术分享 » c++实现——宇宙射线

c++实现——宇宙射线

 

题目描述

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

输入描述

输入第一行包含一个正整数 ,表示宇宙射线会分裂几次
第二行包含n个正整数 ,第ai个数表示第 i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。

输出描述

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

样例输入

4
4 2 2 3

样例输出

39

在这里插入图片描述
在这里插入图片描述

BFS思路

如上图,模拟射线进行前进,但是必须做简化,否则会超时。
选用记忆化搜索;
详细见代码注释

BFS代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include<queue>
using namespace std;
int m_x[] = { 0,1,1, 1, 0, -1, -1, -1 };
int m_y[] = { 1,1,0,-1,-1, -1, 0, 1 };
int n;
int a[35];    
int mp[350][350] = { 0 };  //地图
int vis[350][350][35][9] = { 0 };  //用于记忆化搜素
int ans = 0;
struct position    //存储当前点的信息 
{
	int x;
	int y;
	int n;
	int dir;
};
queue<position> q;  //bfs用到队列
void bfs()
{

	while (!q.empty())
	{
		position now = q.front();
		q.pop();
		for (int i = 1; i <= a[now.n]; i++)
		{
			now.x += m_x[now.dir];
			now.y += m_y[now.dir];
			if (!mp[now.x][now.y])
			{
				mp[now.x][now.y] = 1;
				ans++;
			}
		}
		//向两边发散 记忆化判断
		if (!vis[now.x][now.y][(now.n) + 1][(now.dir + 1) % 8]&&now.n+1<=n)
		{
			vis[now.x][now.y][(now.n) + 1][(now.dir + 1) % 8] = 1;
			q.push({ now.x,now.y,now.n + 1,(now.dir + 1) % 8 });
		}
		if (!vis[now.x][now.y][(now.n) + 1][(now.dir + 7) % 8] && now.n + 1 <= n)
		{
			vis[now.x][now.y][(now.n) + 1][(now.dir + 7) % 8] = 1;
			q.push({ now.x,now.y,now.n + 1,(now.dir + 7) % 8 });
		}
	}
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
	}
	position now;
	now.x = 160;
	now.y = 160;
	now.n = 1;
	now.dir = 0;
	q.push(now);//起始点
	bfs(); 
	printf("%d\n", ans);
	return 0;
}

DFS思路

这个思路是大佬给的启发(手动感谢大佬)。
由于射线的规律性,每次都向两边分散45°,所以一共走八个方向。我们可以每次只走一个方向,走到底后在向后退,每退到一个方向,就沿着上一个来的方向求当前图形的对称!
如图(只举例三步,画画能力有限),首先是红色深度优先遍历到底1 ,2,3(我们规定每次只走右边),
然后回退,首先是第三步关于第二步的对称,在然后是第二步关于第一步的对称。
起始DFS更像是递归求对称。
在这里插入图片描述

DFS代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <list>
#include <algorithm>
using namespace std;
bool a[1600][1600] = { 0 };
int b[35];
int m_x[] = {0,1,1, 1, 0, -1, -1, -1};	//八个行动方向 从向北开始 顺时针走
int m_y[] = {1,1,0,-1,-1, -1, 0, 1};
int ans=0;
int n;
struct position
{   //记录所到位置 及求出的对称点的位置
	int x;
	int y;
	position(){}
	position(int &_x, int &_y)
	{
		x = _x;
		y = _y;
	}
};
list<position> c;  //dfs 
list<position> d;   //回退到的点
//这里为什么用list不用vector 见文末解释。
void symmetry(position& s, int dir)
{//求对称   s 当前位置   dir 上一个方向
	position* tmp=new position();
	if ( dir %8 ==2 || dir % 8 == 6)//关于 平行于x轴的轴 的对称
	{
		for (auto it = d.begin(); it != d.end();it++)
		{//遍历求当前所有回退到的点的对称
			tmp->x= (*it).x; tmp->y = 2 * (s.y) - (*it).y;   //对称公式(擅用搜索引擎)
			if (a[tmp->x][tmp->y]== 0)
			{//如果没被放问过 访问并计数
				a[tmp->x][tmp->y] = 1;
				ans++;
				d.push_front(position(tmp->x, tmp->y));   //加入回退到的序列当中
			}
		}
	}y
	else if (dir % 8 == 0 || dir % 8 == 4)//关于 平行于y轴的轴 的对称
	{
		for (auto it = d.begin(); it !=d.end(); it++)
		{
			tmp->x = 2 * (s.x) - (*it).x; tmp->y = (*it).y;
			if (a[tmp->x][tmp->y] == 0)
			{
				a[tmp->x][tmp->y] = 1;
				ans++;
				d.push_front(position(tmp->x, tmp->y));
			}
		}
	}
	else if (dir % 8 == 1 || dir % 8 == 5)//关于 平行于x=y轴的轴 的对称
	{
		for (auto it = d.begin(); it != d.end() ; it++)
		{
			tmp->x =(*it).y-s.y+s.x; tmp->y = (*it).x +s.y - s.x;
			if (a[tmp->x][tmp->y] == 0)
			{
				a[tmp->x][tmp->y] = 1;
				ans++;
				d.push_front(position(tmp->x, tmp->y));
			}
		}
	}
	else if (dir % 8 == 3 || dir % 8 == 7 )//关于 平行于x=-y轴的轴 的对称
	{
		for (auto it = d.begin(); it != d.end(); it++)
		{
			tmp->x =s.y + s.x-(*it).y  ; tmp->y = s.y +s.x - (*it).x;
			if (a[tmp->x][tmp->y] == 0)
			{
				a[tmp->x][tmp->y] = 1;
				ans++;
				d.push_front(position(tmp->x, tmp->y));
			}
		}
	}
}
void DFS(position s, int len, int dir)
{
	if (len < n)
	{
		for (int i = 0; i < b[len]; i++)
		{
			s.x += m_x[dir];
			s.y += m_y[dir];
			c.push_front(position(s.x,s.y));
		}
		if (len == n - 1)
		{//最后一次 递归终点
			for (int i = 0; i < b[len]; i++)
			{
				d.push_front(c.front());
				if (a[c.front().x][c.front().y] == 0)
				{
					a[c.front().x][c.front().y] = 1;
					ans++;
				}
				c.pop_front();
			}m_x,m_y
			return;
		}
		DFS(s,len+1,(dir+1)%8);    //(dir+1)%8 对照m_x,m_y求下一次向右的方向
		//DFS深度遍历
		symmetry(s,dir);   //将回退到当前图形的图对称

		for (int i = 0; i < b[len]; i++)
		{  //回退c中的序列
			d.push_front(c.front());
			if (a[c.front().x][c.front().y] == 0)
			{
				a[c.front().x][c.front().y] = 1;
				ans++;
			}
			c.pop_front();
		}
	}
	else
		return;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &b[i]);
	}
	position ini;
	ini.x = 800;
	ini.y = 800;
	c.push_front(ini);
	DFS(ini, 0, 0);
	cout << ans << endl;
	return 0;
}

DFS代码二

上课提供的代码,思路类似于BFS,记忆化搜索。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
using namespace std;
int m_x[] = { 0,1,1, 1, 0, -1, -1, -1 };	//x,y 移动方向
int m_y[] = { 1,1,0,-1,-1, -1, 0, 1 };
int n;  //操作数
int a[35];  
int mp[350][350]={0};    //地图
int vis[350][350][35][9]={0};   //记忆化记录
int ans=0;

void dfs(int x,int y,int j,int d)
{
	if(vis[x][y][j][d]) return;
	vis[x][y][j][d]=1;
	for(int i=1;i<=a[j];i++)
	{
		x+=m_x[d];y+=m_y[d];
		if(!mp[x][y])
		{
			ans++;
			mp[x][y]=1;
		}
	}
	if(j<n) 
	{
		dfs(x,y,j+1,(d+1)%8);
		dfs(x,y,j+1,(d+7)%8);
	}	
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);		
	}	
	dfs(160,160,1,0);
	printf("%d\n",ans);
	return 0;
}

总结

1,dfs时为什么用list不用vector

刚开始用的vector,但是同样的思路 怎么写都不对 后来经过输出测试(菜鸟不会debug,只会cout测试);
经过测试,用vector的迭代器遍历的时候,不能在里面加入元素,否则vector的end()迭代器会失效,导致不能正常访问。
总结:vector迭代器的几种失效的情况:
1.当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2.当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操
作返回的迭代器都会失效。
3.当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。

2,BFS常规一定会超时,所以选用记忆化搜索。

转载自原文链接, 如需删除请联系管理员。

原文链接:c++实现——宇宙射线,转载请注明来源!

0