题目描述:
众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着一种叫做苟狗的生物,这种生物天 生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进 ai 个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生 那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击"
输入格式:
输入第一行包含一个正整数n,表示宇宙射线会分裂n次
第二行包含n个正整数 ,第i个数 ai 表示第i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
输出格式:
输出一个数 ans,表示有多少个位置会被降智打击
sample input:
4
4 2 2 3
sample output:
39
个人思路:
考虑这个题的实质,2^n型分裂,
- 暴力的做法:从第一个点开始,把之后分裂的每个点都记下来,然后判断走过的路径长。分析BFS空间复杂度O(2n)DFS时间复杂度为O(2n),这样可以拿40分;
- 剪枝优化:考虑2的指数型分裂的特性,是对称的!也就是说,知道了a,那么他的对称点a’也就知道了
- 第一步思考:刚开始是Y轴正向,那么只计算往一侧的分裂点并且关于对称轴(此刻是Y轴)对称过来就OK了。
- 进一步思考:下一步就是第一步分裂后的子枝,对其进行第一步的操作,发现不对,因为他分裂后的子枝还需要与他的父枝进行对称操作,所以说如果正向操作的话,不是很合理。
- 逆向操作:先从父枝开始,只按照一个方向分裂(我是一只按照往右分裂),一直到头,期间不要visit路径(具体原因后续)。到头后,开始进行对称操作,以最近的对称轴开始,如果对称后的节点没有visit过的,那么标记visit,并且将他放入一个数组中,用于后面对称轴的对称。
- WHY?首先,对于对称轴来说是有优先级的,譬如第一个和最后一个对称轴,如果刚开始就关于第一个对称轴对称,那么后面还没有生成的点自然而然会被忽略掉,所以从最后的对称轴开始,因为关于他分裂后的点只有唯一的路径,再后退就OK了。
- why not 期间 visit:如果在一开始的单一路径时就visit了,那么递归开始后,如果优先级高的轴对称后的点是已经被低于他的对称轴visit了,那么改点就不会加入到数组中,就可能会导致该点关于优先级低的对称轴的对称点的缺失。
代码实现:
#include<iostream>
using namespace std;
int n;
int num = 0;
int a[50];
bool visit[500][500] = { false };
pair<int, int>point[10000];
int sign = -1;//标记每一条对称轴的长度
int rnum = 0;//记录主干线的长度
void DFS(int i, int x, int y, int dx, int dy) {
if (i >= n)return;
int x1 = 0, y1 = 0;
for (int j = 1; j <= a[i]; ++j) {
x1 = x + dx * j;
y1 = y + dy * j;
point[++sign].first = x1;
point[sign].second = y1;
}
rnum = sign;
if (dx == 1 && dy == 1) {// 1 1
int sbegin = sign;
//cout << sign << endl;
DFS(i + 1, x1, y1, 1, 0);
//cout << sign << endl;
for (int s = sign; s >= sbegin; --s) {
if (visit[x1 - y1 + point[s].second][point[s].first - x1 + y1] == false) {
visit[x1 - y1 + point[s].second][point[s].first - x1 + y1] = true;
point[++sign].first = x1 - y1 + point[s].second;
point[sign].second = point[s].first - x1 + y1;
num++;
}
}
}
else if (dx == 1 && dy == 0) {// 1 0
int sbegin = sign;
DFS(i + 1, x1, y1, 1, -1);
for (int s = sign; s >= sbegin; --s) {
if (visit[point[s].first][2 * y1 - point[s].second] == false) {
visit[point[s].first][2 * y1 - point[s].second] = true;
point[++sign].first = point[s].first;
point[sign].second = 2 * y1 - point[s].second;
num++;
}
}
}
else if (dx == 1 && dy == -1) {// 1 -1
int sbegin = sign;
DFS(i + 1, x1, y1, 0, -1);
for (int s = sign; s >= sbegin; --s) {
if (visit[x1 + y1 - point[s].second][x1 + y1 - point[s].first] == false) {
visit[x1 + y1 - point[s].second][x1 + y1 - point[s].first] = true;
point[++sign].first = x1 + y1 - point[s].second;
point[sign].second = x1 + y1 - point[s].first;
num++;
}
}
}
else if (dx == 0 && dy == 1) {// 0 1
int sbegin = sign;
DFS(i + 1, x1, y1, 1, 1);
for (int s = sign; s >= sbegin; --s) {
if (visit[2 * x1 - point[s].first][point[s].second] == false) {
visit[2 * x1 - point[s].first][point[s].second] = true;
point[++sign].first = 2 * x1 - point[s].first;
point[sign].second = point[s].second;
num++;
}
}
}
else if (dx == 0 && dy == -1) {// 0 -1
int sbegin = sign;
DFS(i + 1, x1, y1, -1, -1);
for (int s = sign; s >= sbegin; --s) {
if (visit[2 * x1 - point[s].first][point[s].second] == false) {
visit[2 * x1 - point[s].first][point[s].second] = true;
point[++sign].first = 2 * x1 - point[s].first;
point[sign].second = point[s].second;
num++;
}
}
}
else if (dx == -1 && dy == -1) {// -1 -1
int sbegin = sign;
DFS(i + 1, x1, y1, -1, 0);
for (int s = sign; s >= sbegin; --s) {
if (visit[point[s].second + x1 - y1][y1 - x1 + point[s].first] == false) {
visit[point[s].second + x1 - y1][y1 - x1 + point[s].first] = true;
point[++sign].first = point[s].second + x1 - y1;
point[sign].second = y1 - x1 + point[s].first;
num++;
}
}
}
else if (dx == -1 && dy == 1) {// -1 1
int sbegin = sign;
DFS(i + 1, x1, y1, 0, 1);
for (int s = sign; s >= sbegin; --s) {
if (visit[x1 + y1 - point[s].second][x1 + y1 - point[s].first] == false) {
visit[x1 + y1 - point[s].second][x1 + y1 - point[s].first] = true;
point[++sign].first = x1 + y1 - point[s].second;
point[sign].second = x1 + y1 - point[s].first;
num++;
}
}
}
else if (dx == -1 && dy == 0) {// -1 0
int sbegin = sign;
DFS(i + 1, x1, y1, -1, 1);
for (int s = sign; s >= sbegin; --s) {
if (visit[point[s].first][2 * y1 - point[s].second] == false) {
visit[point[s].first][2 * y1 - point[s].second] = true;
point[++sign].first = point[s].first;
point[sign].second = 2 * y1 - point[s].second;
num++;
}
}
}
}
int main() {
cin >> n;
for (int p = 0; p < n; ++p) {
cin >> a[p];
}
DFS(0, 250, 250, 0, 1);
//cout << rnum << endl;
for (int p = 0; p <= rnum; ++p) {//递归完成后,遍历主干线上的点是否有没遍历的
if (visit[point[p].first][point[p].second] == false)
num++;
}
cout << num << endl;
return 0;
}