AcWing 1128. 信使
\(AcWing\) \(1128\). 信使
一、题目描述
战争时期,前线有 \(n\) 个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。
信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。
指挥部设在 第一个 哨所。
当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。
当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。信在一个哨所内停留的时间可以忽略不计。
直至所有 \(n\) 个哨所全部接到命令后,送信才算成功。
因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他 \(k\) 个哨所有通信联系的话,这个哨所内至少会配备 \(k\) 个信使)。
现在总指挥请你编一个程序,计算出完成整个送信过程 最短需要多少时间 。
输入格式
第 \(1\) 行有两个整数 \(n\) 和 \(m\),中间用 \(1\) 个空格隔开,分别表示有 \(n\) 个哨所和 \(m\) 条通信线路。
第 \(2\) 至 \(m+1\) 行:每行三个整数 \(i、j、k\),中间用 \(1\) 个空格隔开,表示第 \(i\) 个和第 \(j\) 个哨所之间存在 双向 通信线路,且这条线路要花费 \(k\) 天。
输出格式
一个整数,表示完成整个送信过程的最短时间。
如果不是所有的哨所都能收到信,就输出\(-1\)。
二、题目解析
- 单源最短路径,一般采用堆优化版本的\(Dijkstra\)算法
- 最短距离的最大值,也就是 完成整个送信过程的最短时间
三、\(Dijkstra+PII\)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
const int M = 2 * 210; //无向边,开两倍
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int d[N];
bool st[N];
int dijkstra() {
int res = 0, cnt = 0;
//初始化为正无穷
memset(d, 0x3f, sizeof d);
d[1] = 0; // 1号点为出发点,距离为0
//小顶堆
priority_queue<PII, vector<PII>, greater<>> q;
q.push({0, 1});
while (q.size()) {
auto u = q.top();
q.pop();
if (st[u.second]) continue; // Dijkstra第一次出队列为最小值
st[u.second] = true;
//求所有最短距离的最大值
res = max(res, u.first);
//记录到达的节点个数
cnt++;
for (int i = h[u.second]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > d[u.second] + w[i]) {
d[j] = d[u.second] + w[i];
q.push({d[j], j});
}
}
}
//如果可以成功到达每个节点,返回最短距离的最大值,否则返回-1
return cnt == n ? res : -1;
}
int main() {
//加快读入
cin.tie(0), ios::sync_with_stdio(false);
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
printf("%d\n", dijkstra());
return 0;
}
四、\(Dijkstra\)+结构体
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int M = 2 * 210; //无向边,开两倍
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int d[N];
bool st[N];
//定义结构体
struct Node {
int id; //结点ID
int dist; //和起点的距离
bool operator<(const Node &t) const {
return dist > t.dist; //重载小于号,父节点距离 大于 孔洞节点 距离,则大元素向下,小元素向上,小顶堆
}
};
int dijkstra() {
int res = 0, cnt = 0;
//初始化为无法到达
memset(d, 0x3f, sizeof d);
d[1] = 0; // 1号点为出发点,距离为0
//小顶堆
priority_queue<Node> q;
q.push({1, 0});
while (q.size()) {
auto u = q.top();
q.pop();
if (st[u.id]) continue;
st[u.id] = true;
res = max(res, u.dist);
cnt++; //出队列时统计个数
//枚举每个出边
for (int i = h[u.id]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > d[u.id] + w[i]) {
d[j] = d[u.id] + w[i];
q.push({j, d[j]});
}
}
}
return cnt == n ? res : -1;
}
int main() {
//加快读入
cin.tie(0), ios::sync_with_stdio(false);
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
printf("%d\n", dijkstra());
return 0;
}
五、\(SPFA\)解法
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int M = 2 * 210; //无向边,开两倍
const int INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int d[N];
bool st[N];
//定义结构体
struct Node {
int id; //节点ID
int dist; //和起点的距离
bool operator<(const Node &t) const {
return dist > t.dist; //重载小于号,父节点距离 大于 孔洞节点 距离,则大元素向下,小元素向上,小顶堆
}
};
// spfa模板
void spfa() {
queue<int> q; //队列
memset(d, 0x3f, sizeof d); //将所有距离初始化为无穷大
//出发点的距离清零
d[1] = 0;
q.push(1); //出发点入队列
st[1] = true; //出发点标识已使用
while (q.size()) {
int u = q.front();
q.pop();
st[u] = false;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > d[u] + w[i]) {
d[j] = d[u] + w[i];
if (!st[j]) {
st[j] = true;
q.push(j);
}
}
}
}
}
// 14 ms
int main() {
//加快读入
cin.tie(0), ios::sync_with_stdio(false);
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
spfa();
int res = 0;
for (int i = 2; i <= n; i++) {
if (d[i] == INF) {
res = -1;
break;
}
res = max(res, d[i]);
}
printf("%d\n", res);
return 0;
}
六、\(Floyd\)解法
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
const int INF = 0x3f3f3f3f;
int n, m;
int d[N][N];
// 45 ms
int main() {
//加快读入
cin.tie(0), ios::sync_with_stdio(false);
cin >> n >> m;
//初始化邻接矩阵
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= n; i++) d[i][i] = 0;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
d[a][b] = d[b][a] = min(d[a][b], c);
}
// 5行代码的Floyd
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
int res = 0;
for (int i = 1; i <= n; i++)
if (d[1][i] == INF) {
res = -1;
break;
} else
res = max(res, d[1][i]);
printf("%d\n", res);
return 0;
}