程序设计思维(CSP模拟题) 可怕的宇宙射线(宽度优先搜索+剪枝)

题目

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

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

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

样例输入:
4
4 2 2 3

样例输出:
39

在这里插入图片描述

思路

用二维数组表示宇宙射线在二维平面上的传播。
如图
在这里插入图片描述
可以发现,射线在某一坐标(x, y)时,可能有8种不同的传播方向:上(①)、下(⑤)、左(⑦)、右(③)、左上(⑧)、左下(⑥)、右上(②)、右下(④)。
当射线运动到某一点进行分裂时,相应地,有如下8种可能结果:
在这里插入图片描述
例如,当射线分裂时的方向为①(上)时,分裂后的两个方向为⑧(左上)和②(右上)。
用数组a[31]存放射线每一次分裂后要走过的距离。
由题可知,射线最多分裂30次,每次分裂最多走5格。因此,射线朝一个方向最多走150格。于是可以定义一个平面二维矩阵为visit[400][400],用于记录射线是否经过。
笔者采用了宽度优先搜索。
如果不进行任何处理直接宽度优先搜索,则需要搜索多达230=1,073,741,824(10亿)条分裂的路径,这显然是无法接受的。
为了节约时间,可以开一个四维数组M[400][400][9][31](用空间换时间)。例如,当射线在坐标(i, j)处以方向d进行第s次分裂时,若M[i][j][d][s] = true,则说明在这之前已经有射线进行过这样的分裂了,因此无需接着往下进行搜索(剪枝);否则需要接着往下进行搜索,同时将M[i][j][d][s]置为true。
宽搜的方法就是使用一个队列,每次从队列中取出一个“路径”,将路径所经过的坐标在visit二维数组中标为true,同时这个路径还会分裂成另外两个路径,又将这两个路径放回队列中……循环至队列为空。
最后统计visit中有多少个true,即为宇宙射线经过的所有位置的总数。

代码

#include <iostream>
#include <queue>
#include <cstring>
#define MAX 400
#define DIR 8+1
#define INDEX 30+1

using namespace std;

bool visit[MAX][MAX];
bool trace[MAX][MAX][DIR][INDEX];
int n;
int a[35];
int cnt;

int minX = MAX / 2, maxX = MAX / 2;
int minY = MAX / 2, maxY = MAX / 2;

struct explodeTrack {
	int x;
	int y;
	int dir;
	int stepIndex;
	explodeTrack(int theX, int theY, int theDir, int theStepIndex) : x(theX), y(theY), dir(theDir), stepIndex(theStepIndex) {}
	bool operator < (const explodeTrack& t)const
	{
		if (x != t.x) return x < t.x;
		else if (y != t.y) return y < t.y;
		else if (dir != t.dir) return dir < t.dir;
		else return stepIndex < t.stepIndex;
	}
};

// dx[0]、dy[0]不用
int dX[9] = { 0,-1,-1, 0, 1, 1, 1, 0,-1 };
int dY[9] = { 0, 0, 1, 1, 1, 0,-1,-1,-1 };

// nextDir1[0]、nextDir2[0]不用
int nextDir1[9] = { 0,8,1,2,3,4,5,6,7 };
int nextDir2[9] = { 0,2,3,4,5,6,7,8,1 };

queue<struct explodeTrack> q;

void printBoolMatrix(bool M[MAX][MAX])
{
	for (int i = 0; i < MAX; i++) {
		for (int j = 0; j < MAX; j++) {
			cout << M[i][j] << " ";
		}
		cout << endl;
	}
}

void compare(int x, int y)
{
	if (minX > x) minX = x;
	if (maxX < x) maxX = x;
	if (minY > y) minY = y;
	if (maxY < y) maxY = y;
}

void track(explodeTrack& p)
{
	int x = p.x;
	int y = p.y;
	int dir = p.dir;
	int index = p.stepIndex;
	int step = a[index];
	int nextIndex = index + 1;
	int nextDirection1 = nextDir1[dir];
	int nextDirection2 = nextDir2[dir];

	// 剪枝
	if (trace[x][y][dir][index]) return;

	trace[x][y][dir][index] = true;

	int dx = dX[dir];
	int dy = dY[dir];

	for (int s = 0; s < step; s++) {
		x += dx;
		y += dy;
		visit[x][y] = true;
		compare(x, y);
	}

	if (nextIndex >= n) return;
	q.push(explodeTrack(x, y, nextDirection1, nextIndex));
	q.push(explodeTrack(x, y, nextDirection2, nextIndex));

	return;
}

int countVisit()
{
	cnt = 0;
	for (int i = minX; i <= maxX; i++)
		for (int j = minY; j <= maxY; j++)
			if (visit[i][j]) cnt++;
	return cnt;
}


void reset()
{
	memset(visit, false, sizeof(visit));
	memset(trace, false, sizeof(trace));
	memset(a, 0, sizeof(a));
	while (!q.empty()) q.pop();
}

int main()
{
#if(1)
	cin >> n;

	reset();

	for (int i = 0; i < n; i++)
		cin >> a[i];

	// 起点
	explodeTrack start = explodeTrack(MAX / 2, MAX / 2, 1, 0);

	q.push(start);

	while (!q.empty()) {
		explodeTrack t = q.front();
		q.pop();
		track(t);
	}

	cout << countVisit() << endl;
#endif

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值