团体天梯练习 L3-008 喊山
L3-008 喊山
喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂—喂喂喂……”的呼唤。呼唤声通过空气的传递,回荡于深谷之间,传送到人们耳中,发出约定俗成的“讯号”,达到声讯传递交流的目的。原来它是彝族先民用来求援呼救的“讯号”,慢慢地人们在生活实践中发现了它的实用价值,便把它作为一种交流工具世代传袭使用。(图文摘自:http://news.xrxxw.com/newsshow-8018.html)

一个山头呼喊的声音可以被临近的山头同时听到。题目假设每个山头最多有两个能听到它的临近山头。给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方。
输入格式:
输入第一行给出3个正整数
输出格式:
依次对于输入中的每个被查询的山头,在一行中输出其发出的呼喊能够连锁传达到的最远的那个山头。注意:被输出的首先必须是被查询的个山头能连锁传到的。若这样的山头不只一个,则输出编号最小的那个。若此山头的呼喊无法传到任何其他山头,则输出
输入样例:
7 5 4
1 2
2 3
3 1
4 5
5 6
1 4 5 7
输出样例:
2
6
4
0
解题思路
这道题一开始我理解错了题意,题干中给出的比较关键的条件在于 一个山头呼喊的声音可以被临近的山头同时听到。而我一开始没太在意这个,直接通过样例来理解题目意思了,而正是因为如此,导致我理解错题意,一直只能拿到 21 分。(不过我觉得陈越老师有时应当解释解释样例哈,不然真的老是理解错题目呜呜呜呜)
我先讲讲我一开始错误的理解,对于这个样例,通过画图之后可以看出有三个连通块,其中第一个连通块是存在环的,而第一个查询的节点是1,从1传到最远的地方答案给出的是2,而题目中说的是传到距离最远的点,所以,我就理解成了,因为连通块中包含环,所以1到达其它任意点的距离都是无限远,此时取编号最小的点输出即可;而如果查询的点的所属连通块内不含环,那么就可以通过
所以,延续着这个错误的理解,我先用一个
下面是我错误的杰作,21分呜呜呜呜呜呜
/* 一切都是命运石之门的选择 El Psy Kongroo */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
typedef pair<double, double> pdd;
typedef pair<string, int> psi;
//typedef __int128 int128;
#define PI acos(-1.0)
#define x first
#define y second
//int dx[4] = {1, -1, 0, 0};
//int dy[4] = {0, 0, 1, -1};
const int inf = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 10010, M = 4 * N;
int h[N], e[M], ne[M], idx;
int n, m, k;
int dist[N];
int ind[N]; //度数
bool vis[N];
map<int, bool> hasCir; //判断是否存在环
vector<int> block[N]; //每个连通块的点
int cnt;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
ind[b] ++ ;
}
//dfs求连通块
void dfs(int u){
vis[u] = true;
block[cnt].push_back(u);
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(vis[v]) continue;
dfs(v);
}
}
//dfs求源点到各点距离
void dfs2(int u, int fa){
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(v == fa) continue;
dist[v] = dist[u] + 1; //自顶向下
dfs2(v, u);
}
}
//拓扑排序判断无向图是否存在环
bool topo(int id){
int cnt = 0;
queue<int> q;
for(auto &v : block[id])
if(ind[v] <= 1) q.push(v); //度数小于等于1的入队
while(!q.empty()){
int u = q.front();
q.pop();
cnt ++ ;
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
ind[v] -- ;
if(ind[v] == 1) q.push(v);
}
}
return cnt != (int)block[id].size();
}
//若存在环 需要连通块内编号最小且不是源点的点
int find_min(int i, int u){
int res = inf;
for(auto &v : block[i])
if(v != u) res = min(v, res);
return res;
}
void solve(int u){
int id = 0;
for(int i = 1; i <= cnt; i ++ )
if(find(block[i].begin(), block[i].end(), u) != block[i].end()){
id = i; //找到查询点所属连通块
break;
}
if(hasCir[id]) {
int minv = find_min(id, u);
if(minv == inf) cout << 0 << endl;
else cout << minv << endl;
return;
}
memset(dist, 0, sizeof(dist));
dfs2(u, -1);
int res = 0, ansv = 0;
for(auto &v : block[id])
if(v != u && dist[v] > res) res = dist[v], ansv = v;
cout << ansv << endl;
return;
}
void show(int i){
for(auto &v : block[i]) cout << v << ' ';
cout << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
memset(h, -1, sizeof(h));
cin >> n >> m >> k;
while(m -- ){
int u, v; cin >> u >> v;
add(u, v), add(v, u);
}
for(int i = 1; i <= n; i ++ )
if(!vis[i]) cnt ++ , dfs(i), hasCir[cnt] = topo(cnt);
while(k -- ){
int u; cin >> u;
solve(u);
}
return 0;
}
由于这个错误思想,我写了两个
所以,这告诉我们真的要认真读题,不然就像我这样......在错误的道路上越走越深。
关键的条件在于 一个山头呼喊的声音可以被临近的山头同时听到,根据这一点,我们其实可以知道,这个其实是广度优先搜索的操作,就是从一个点开始,然后扩散到周围所有邻接的点,然后再扩散......所以这其实就是
最后的解答还是比较简单的。
/* 一切都是命运石之门的选择 El Psy Kongroo */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
typedef pair<double, double> pdd;
typedef pair<string, int> psi;
//typedef __int128 int128;
#define PI acos(-1.0)
#define x first
#define y second
//int dx[4] = {1, -1, 0, 0};
//int dy[4] = {0, 0, 1, -1};
const int inf = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 10010, M = 4 * N;
int h[N], e[M], ne[M], idx;
int dist[N];
bool vis[N];
int n, m, k;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int bfs(int src){
int res = src, maxd = 0;
queue<int> q;
q.push(src);
vis[src] = true;
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(vis[v]) continue;
dist[v] = dist[u] + 1;
q.push(v);
vis[v] = true;
if(dist[v] > maxd) maxd = dist[v], res = v;
else if(dist[v] == maxd && v < res) res = v; //编号更小
}
}
return res == src ? 0 : res; //如果是独立的点 输出0
}
int solve(int u){
memset(dist, 0, sizeof(dist));
memset(vis, 0, sizeof(vis));
return bfs(u);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m >> k;
memset(h, -1, sizeof(h));
while(m -- ){
int u, v; cin >> u >> v;
add(u, v), add(v, u);
}
while(k -- ){
int u; cin >> u;
cout << solve(u) << endl;
}
return 0;
}
一切都是命运石之门的选择,本文章来源于博客园,作者:MarisaMagic,出处:https://www.cnblogs.com/MarisaMagic/p/17342004.html,未经允许严禁转载
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具