week 4 CSP模拟 C 宇宙射线 Gym - 270437F

题意:

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

输入描述
输入第一行包含一个正整数 ,表示宇宙射线会分裂 次 第二行包含n个正整数 ,第 个数 表示第 次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
输出描述
输出一个数 ,表示有多少个位置会被降智打击

样例输入
4
4 2 2 3
样例输出
39
数据点说明 数据点 n 10% <=10 40% <=20 100% <=30

时间与内存限制 每个测试点 1000ms 262144KB


思路:

由题可知,射线是在一个二维网格图中穿行,并且走过一定距离便会统一分裂,按照各自方向再继续前行。这是一道变样的地图题,与普通的地图题相比便是走的方向不同,走的距离不同,不是一个点在走而是每次分裂便会由一条射线分裂出新方向的两条射线。

射线走过相应的距离后,便会分裂然后按照给的距离继续沿着新的方向前进,将分裂前的射线看作一层,分裂后的射线看作新的一层,每次分裂就是一层一层的拓展,于是我们便可以用BFS的思想解题。普通迷宫的BFS解法是采用队列记录中间节点,然后向上下左右四个方向拓展。这里同样采用队列记录射线在分裂时的点,不过由于题目的特殊性,这些射线在分裂的点分裂后的前进方向与原先的方向有关,所以设立一个结构体用以表示这些点,结构体里的变量是一个pair对象用以表示点的坐标,还有另一个pair对象以类似向量的方式表示方向。

题意表示最多分裂30次,每次分裂最长走五个单位距离,因此这个网格状地图最多是300*300的。关于点分裂之后的射线方向,可以按照这个点的方向按照规律得到。在队列里记录下最先分裂的点,然后计算得出这个点分裂出的射线走到哪些点会再次分裂,将这些点继续加入队列中,一直这样进行,直至分裂结束。但是这样必定会TLE,因为重复走的路径太多了,我们需要进行剪枝。

一层射线走完后会在各自的分裂点进行分裂,但是会出现这种情况,这一层射线的分裂点中,存在由来源不同的射线,确是以完全相同的距离和方向走了这一层,于是这些射线在之后的分裂前行都会是完全重复的,这样的点就需要剪枝,每层分裂进行剪枝,留下的分裂点有这样的条件:即使点的位置一样,但是方向也要不一样。为了判断在一层中某个方向的某个点的此时状态是否被同属于这一层的一个完全相同的点已经经历过了,我们设立一个bool数组,bool onelayer[301 * 301 * 8] = {false},将八种方向分别对应0到7的不同的值,每一个点在这个数组中占了8个元素,代表着这个点的8种方向,若是某个元素为true,则表示这一层的这个点的这个状态已经被经历过了,于是这个点便不加入到队列中去,一层结束后要将这个数组元素全部重置为false,以便于下一层使用。


总结:

对于这种路径探索题,可以采取BFS的方法解决,但是一定要注意剪枝,对重复进行的状态进行处理。

要仔细读题,例如这道题中,便隐藏着实际网格点地图的大小最多便是300*300的这种关键信息。


代码:

#include<iostream>
#include<utility>
#include<map>
#include<queue>
#include<string.h>
#define max 301
using namespace std;
bool onelayer[301 * 301 * 8];
long long giveup = 0;
long long tempGiveup;

struct p {
	pair<int, int>Point;
	pair<int, int>lo;

	p(int p1, int p2, int p3, int p4)
	{
		Point.first = p1;
		Point.second = p2;
		lo.first = p3;
		lo.second = p4;
	}
	p(const p& t)
	{
		Point.first = t.Point.first;
		Point.second = t.Point.second;
		lo.first = t.lo.first;
		lo.second = t.lo.second;
	}

};

int ans = 0;
queue<p>point;
bool arrived[max][max];
map<pair<int, int>, int>where;

void func(int& tx, int& ty, p& m, int& nowstep,int t)
{
	for (int i = 1; i <= nowstep; i++)
	{
		if (arrived[m.Point.first + i * tx][m.Point.second + i * ty] == false)
		{
			arrived[m.Point.first + i * tx][m.Point.second + i * ty] = true;
			ans++;
		}
	}
	int nowx = m.Point.first + nowstep * tx;
	int nowy = m.Point.second + nowstep * ty;

	if (onelayer[(nowx * 301 + nowy) * 8 + where[pair<int, int>(tx, ty)]] == true)
	{
		tempGiveup++;
		return;
	}
	p temp2(nowx, nowy, tx, ty);
	point.push(temp2);
	onelayer[(nowx * 301 + nowy) * 8 + where[pair<int, int>(tx, ty)]] = true;
}

int main()
{
	memset(arrived, false, sizeof(bool) * max * max);
	memset(onelayer, false, sizeof(bool) * 301 * 301 * 8);
	where.insert(pair<pair<int, int>, int>(pair<int, int>(0, 1), 0));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(0, -1), 1));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(1, 0), 2));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(-1, 0), 3));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(1, 1), 4));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(-1, 1), 5));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(1, -1), 6));
	where.insert(pair<pair<int, int>, int>(pair<int, int>(-1, -1), 7));


	int n;
	cin >> n;
	int* step = new int[n];
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &step[i]);
	}

	for (int i = 0; i < step[0]; i++)
	{
		arrived[0+150][i+150] = true;
		ans++;
	}
	
	
	p temp1(150, 149 + step[0], 0, 1);
	point.push(temp1);

	long long now = 0;
	long long to = 2;
	int t = 1;

	while (!point.empty())
	{
		p m = point.front();
		point.pop();
		now++;
		int nowstep = step[t];
		int x = m.lo.first;
		int y = m.lo.second;
		
		if (x == 0 || y == 0)
		{
			if (x == 0) {
				int tx = -1;
				int ty = 1*y;
				func(tx, ty, m, nowstep,t);

				tx = 1;
				func(tx, ty, m, nowstep,t);
			}
			else
			{
				int tx = 1 * x;
				int ty = -1;
				func(tx, ty, m, nowstep,t);

				ty = 1;
				func(tx, ty, m, nowstep,t);
			}
		}
		else
		{
			int tx = x;
			int ty = 0;
			func(tx, ty, m, nowstep,t);
			
			tx= 0;
		    ty = y;
			func(tx, ty, m, nowstep,t);
		}

		if (now + giveup == to/2)
		{
			if (t == n-1)break;
			memset(onelayer, false, sizeof(bool) * 301 * 301 * 8);
			giveup = giveup * 2;
			giveup += tempGiveup;
			tempGiveup = 0;
			t++;
			to = to * 2;
			now = 0;
			
		}

	}
	cout << ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值