【bfs+链式向前星】防御僵尸(defend)计蒜客 - 45288
题目:
A 国有 n 座城市,n−1 条双向道路将这些城市连接了起来,任何两个城市都可以通过道路互通。
某日,A 国爆发了丧尸危机,所有的幸存者现在都聚集到了 A 国的首都(首都是编号为 1 的城市)。而其它城市的丧尸会时不时地向首都发起进攻。
为了抵御丧尸的攻击,幸存者在 A 国的每座城市都修建了防御工事。编号为 i 的城市,其防御工事强度为 di。
当一只丧尸决定进攻首都时,它会从某城市 u(u≠1) 沿经过道路数量最少的路径前往首都。沿途,这只丧尸会试图突破当地的防御工事。
对于某座防御工事,如果丧尸的凶猛值不小于该防御工事的强度,丧尸就会突破这座防御工事,否则丧尸会被阻截在此。
注意:如果丧尸突破了某城市的防御工事,并不代表此防御工事被破坏。该防御工事在面对后面来的丧尸的时候还会保持原先的强度。
输入格式
第一行,包含一个正整数 n。
第二行包含 n 个正整数,第 i 个正整数表示编号为 i 的城市的防御工事的强度 d_i。
接下来 n-1 行,每行有两个空格隔开的整数 u,v,表示编号为 u 的城市与编号为 v 的城市之间有一条双向道路。
接下一行,包含一个正整数 q,表示发生了 q 次丧尸进攻首都的事件。
接下来 q 行,每行两个整数 u,x,表示有一只凶猛值为 x 的丧尸从编号为 u 的城市出发,进攻首都。
输出格式
输出 q 行,按照输入给出的顺序输出每个丧尸分别被阻截在了哪座城市。如果丧尸突破了所有的防御工事,攻入了首都,则请输出
The zombie ate your brains!
。
数据范围
本题包含 10 个测试点
对于前三个测试点,保证 n≤100
对于第四个到第六个测试点,保证第 i 条边连接了城市 i 和城市 i+1
对于全部测试点,保证 1≤n≤2×10^5,1≤di,x≤100,u≠1,1≤q≤2×10^5
输出时每行末尾的多余空格,不影响答案正确性
要求使用「文件输入输出」的方式解题,输入文件为 defend.in
,输出文件为 defend.out
Sample Input
5
8 2 7 1 4
1 2
1 3
3 4
3 5
5
2 1
2 9
3 6
4 5
5 7
Sample Output
2
The zombie ate your brains!
3
3
1
题解:
很容易发现该题是最短路径问题,而且的记录路径的最短路径问题。
根据 题目我们有两种思路,第一个是对每个询问单独处理求点到点的最短路在寻路过程中计算结果,第二种是先对图进行处理求出点到所有点的最短路。
第一种方法,求两点之间最短距离的比较稳定的算法是Dijkstra算法,复杂度是O(m*logn).而后有q个查询复杂度应该是O(q*m*logn),q,m,n最大值都为2e5,明显会超时,由于这题图的特殊性,它是一个无权图,就是说边的权值默认是1,其实还可以用bfs和dfs求两点最短路,但时间复杂度也不理想因为拥有q个询问,就算复杂度为线性还是会超时。
第二种方法是,先预处理,求点到其他点的最短路径算法还是是Dijkstra算法,这个算法是以bfs为基础改进而来的,同样因为这题图的特殊性,用bfs就可以计算出点到其他点的最短路,然后最大的问题是如何储存路径,如果之间按点来储存所有路径,那在最坏情况(图为链状,比如 图是 1-2-3-4,对点储存就是:2:2-1,3:3-2-1,4:4-3-2-1。)空间复杂度会达到n^2级别,很有可能超内存,同时数据复制带来的的时间复杂度也会提高。从刚刚的链状图的路径储存,我们会发现有很多重复的部分,就利用他来压缩路径。
构造一个数组path[i]=j,其中i指第i个点的最短路的最后一个路径是i-j。用这种方法就可以将最短路路径完美的储存在数组里面。
|0,1,2,3,4,5,6|
用它获得路径的方法也很简单,比如我们有这样子的数组path[]={0,0,1,2,2,6,1},我们要获得从5到1的路径,path[5]=6,path[6]=1,说明5到1的路径是5-6-1,同样,我们要获得3到1的路径,path[3]=2,path[2]=1,所以3到1的路径是,3-2-1。
加入一个新的点也很简单,只要找到这个点的最短路的最后一步直接加在数组后面就可以,这也正好符合了bfs搜索的方法。
因为这题卡时间复杂度比较厉害,所以再提供3个优化思路。
一 链式向前星存边:
储存图不要使用常用的方便的vector<int>e[n],因为vector的分配内存和改变迭代器很消耗时间。所以使用链式向前星来储存,会很大程度的优化时间复杂度,一定程度的优化空间复杂度。
链式向前星和前文中储存路径的方法有些许类似,实际上链式向前星是一种数组模拟单向链表的操作。
一般的链式向前星的主体是3个数组加一个整数,head[],to[],next[],k(一般会写成结构体,如果有权值可在加入一个数组储存权值),
head[i]=j,指点i的边的最后一个索引是j to[i]=j,指索引i指向的点是j next[i]=j,指索引i的下一个索引是j,k指边的个数.
使用前先对索引初始化,将所有索引置为-1,即将head,next置为-1;
加入新边的操作是给予新边一个索引(就是k),将新边的目标点存入相应的to数组里面,将相应的next更新为head的值,然后将head的值更新为这个新边的索引。
遍历结构体的方法与之前的路径遍历方法类似。
包装的结构体
struct Edge {
int to, next;
};
struct CStar { Edge edge[];
int head[];
int k;
CStar() {
k = ;
for (int i = ; i < ; i++) {
head[i] = -;
edge[i].next = -;
}
}
void push(int a, int b) {
edge[k].to = b;
edge[k].next = head[a];
head[a] = k++;
}
};
遍历边的操作
for (int i = e.head[x]; ~i; i = e.edge[i].next)
...
二 路径最大值:
在每一个查询中遍历路径进行计算也有挺高的复杂度,特别是在僵尸凶猛值很大时,这样它就要遍历一整条路径。我们可以用一些方法来避免出现遍历整条路径的情况,我们可以在计算最短路径时顺势算出最短路径中的最大防御值,并储存在数组中。这样我们最后进行查询时如果僵尸凶猛值大于等于这个路径防御最大值就可以直接输出“The zombie ate your brains!”
三 路径优化:
这个方法理论上可以一定程度减少复杂度,但我个人在本题样例上使用的并不那么有效,但也提出了作为一个思路扩展。
这个思路是基于当你大于等于一个值x后就肯定大于等于一个小于等于x的值。
所以我们可以以此思路对路径进行优化,使路径的每一条路变得严格递增,从而优化查询时间。
例子:
可以变化成
可以在不改变结果的情况下降低树的深度,从而降低查询时间。
用文字描述过程就是,将一个点连到它最短路上第一个大于等于它值的数上,直到它达到首都(也就是编号为1的点)
AC代码:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cctype> using namespace std; struct Edge {
int to, next;
}; struct CStar { Edge edge[];
int head[];
int k;
CStar() {
k = ;
for (int i = ; i < ; i++) {
head[i] = -;
edge[i].next = -;
}
}
void push(int a, int b) {
edge[k].to = b;
edge[k].next = head[a];
head[a] = k++;
}
}; CStar e; int d[];
int path[];
int book[];
int ld[];
queue<int>qu; int main() {
#if 1
freopen("defend.in", "r", stdin);
freopen("defend.out", "w", stdout);
#endif int n, q, a, b;
int x, u, flag, len, it;
scanf("%d", &n); for (int i = ; i <= n; ++i) {
scanf("%d", d + i);
} for (int i = ; i < n; ++i) {
scanf("%d%d", &a, &b);
e.push(a, b);
e.push(b, a);
} qu.push();
book[] = ;
path[] = ;
ld[] = d[];
while (!qu.empty()) {
x = qu.front(); for (int i = e.head[x]; ~i; i = e.edge[i].next) {
it = e.edge[i].to;
if (book[it] == ) {
book[it] = ;
path[it] = x;
ld[it] = max(ld[x], d[it]);
qu.push(it);
}
}
qu.pop(); } scanf("%d", &q);
while (q--) {
flag = ;
scanf("%d%d", &u, &x);
if (ld[u] <= x)printf("The zombie ate your brains!\n");
else {
while (u) {
if (x < d[u]) {
flag = ;
printf("%d\n", u);
break;
}
u = path[u];
}
if (flag)printf("The zombie ate your brains!\n");
}
} return ;
}
【bfs+链式向前星】防御僵尸(defend)计蒜客 - 45288的更多相关文章
- 图论 ---- spfa + 链式向前星 ---- poj 3268 : Silver Cow Party
Silver Cow Party Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 12674 Accepted: 5651 ...
- Tarjan模版(链式向前星表示方法)
这道模版用到了链式向前星表示法: struct node { int v,next; }edge[]; void add(int x,int y) { edge[++cnt].next=heads[x ...
- 【数据结构】链式向前星知识点&代码
代码: struct NODE{ int to; int nxt; int c; }node[MM];//链式向前星 ; void add(int a,int b,int c){ node[lcnt] ...
- 图论 --- spfa + 链式向前星 : 判断是否存在正权回路 poj 1860 : Currency Exchange
Currency Exchange Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 19881 Accepted: 711 ...
- 图论 --- spfa + 链式向前星 (模板题) dlut 1218 : 奇奇与变形金刚
1218: 奇奇与变形金刚 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 130 Solved: 37[Submit][Status][Web Boa ...
- 计蒜客 39272.Tree-树链剖分(点权)+带修改区间异或和 (The 2019 ACM-ICPC China Shannxi Provincial Programming Contest E.) 2019ICPC西安邀请赛现场赛重现赛
Tree Ming and Hong are playing a simple game called nim game. They have nn piles of stones numbered ...
- 计蒜客 一维坐标的移动(BFS)
在一个长度为 n 的坐标轴上,蒜头君想从 A 点 移动到 B 点.他的移动规则如下: 向前一步,坐标增加 1. 向后一步,坐标减少 1. 跳跃一步,使得坐标乘 2. 蒜头君不能移动到坐标小于 0 或大 ...
- 计蒜客模拟赛D2T3 蒜头君救人:用bfs转移状压dp
题目链接:https://nanti.jisuanke.com/t/16444 题意: 蒜头君是一个乐于助人的好孩子,这天他所在的乡村发生了洪水,有多名村民被困于孤岛上,于是蒜头君决定去背他们离开困境 ...
- 计蒜客 无脑博士 bfs
题目链接无脑博士的试管们 思路:直接模拟倒水过程即可,但是需要记忆判断当前的情况是否已经处理过.dfs和bfs都ok AC代码 #include <cstdio> #include < ...
随机推荐
- 流媒体学习计划表——pr
参考教程 视频:b站oeasy 书籍:<adobe premiere pro cc 2018经典教程> 学习教训 一定要多做--实践是检验真理的唯一标准 书籍补充理论知识,视频讲究实操(理 ...
- 28_链表插入和删除算法的演示.swf
#include<stdio.h> #include<malloc.h> #include <stdio.h> #include <stdlib.h> ...
- 一个工作了四年的java程序员的心得体会
年底了,该给自己写点总结了!从毕业到现在已经快4年啦,一直在Java的WEB开发行业混迹.我不是牛人,但是自我感觉还算是个合格的程序员,有必要写下自己将近4年来的经历,给自我以提示,给刚入行的朋友提供 ...
- java scoket Blocking 阻塞IO socket通信一
package bhz.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; p ...
- 2020/6/11 JavaScript高级程序设计 DOM
DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序接口).他描绘了一个层次化的节点树,允许开发人员添加.移除和修改页面的某一部分. 10.1 节点层次 DOM将任何HTML和XML ...
- LeetCode57. 插入区间
对于新插入的区间newInterval,原区间列表intervals可以分为三个部分: 左边与newInterval不重合的区间,这些区间直接加入结果数组中: 中间与newInterval重合的区间, ...
- Redis系列(九):数据结构Hash源码解析和HSET、HGET命令
2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...
- 手写SpringMVC框架(二)-------结构开发设计
续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...
- Yolo车辆检测+LaneNet车道检测
Yolo车辆检测+LaneNet车道检测 源代码:https://github.com/Dalaska/Driving-Scene-Understanding/blob/master/README.m ...
- 树莓派搭建Nexus2私服
使用树莓派搭建Nexus2私服需要的材料有: 树莓派3B+(或者4B) 移动硬盘一个 1. 下载nexus2.x安装包 由于nexus2.x官方的启动环境并不支持arm架构的树莓派,所以这里采用tom ...