文章目录
P1119 灾后重建
一道很好的题目,考察对于Floyd算法的理解,按照村庄重建好的顺序来进行更新最短距离。
最后输出的时候需要特判:两个村庄是否都重建好。
一份很好的题解:https://www.luogu.com.cn/problem/solution/P1119
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f, N = 205;
int ma[N][N], tim[N];
int n, m, st, ed;
void init()
{
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
ma[i][j] = (i == j ? 0 : INF);
}
void Floyd(int k)
{
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
ma[i][j] = min(ma[i][j], ma[i][k] + ma[k][j]);
}
int main(void)
{
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> tim[i];
}
init();
int a, b, c;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
if(ma[a][b] > c)
ma[a][b] = ma[b][a] = c;
}
int q, x, y, t;
// 当前重建好的村庄(引入的第三点)
int idx = 0;
cin >> q;
while (q--) {
scanf("%d%d%d", &x, &y, &t);
// 重建村庄
while (tim[idx] <= t && idx < n) {
Floyd(idx);
idx++;
}
// 询问的时间小于重建好的时间
if (tim[x] > t || tim[y] > t) {
cout << "-1" << endl;
} else if (ma[x][y] == INF) {
cout << "-1" << endl;
} else {
cout << ma[x][y] << endl;
}
}
return 0;
}
P3371【模板】单源最短路径(弱化版)
数据最大为1e4,所以需要用到堆优化+邻接表
//邻接表写法
#include <bits/stdc++.h>
#define PUSH(x,y,z) G[x].push_back(P(y,z)) // 宏函数
using namespace std;
const int INF = 0x3f3f3f3f, N = 10005;
typedef pair<int, int> P;
int n, m, st, ed;
vector<P> G[N];
int dis[N];
bool vis[N];
void init()
{
for(int i = 0; i < N; i++)
G[i].clear();
memset(dis, INF, sizeof dis);
memset(vis, false, sizeof vis);
}
void Dijk()
{
dis[st] = 0;
priority_queue<P, vector<P>, greater<P> > que;
que.push(P(0, st));
while(que.size()) {
P p = que.top(); que.pop();
int u = p.second;
if(vis[u])
continue;
vis[u] = true;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].first;
int cost = G[u][i].second;
if(!vis[v] && dis[v] > dis[u] + cost) {
dis[v] = dis[u] + cost;
que.push(P(dis[v], v));
}
}
}
}
int main(void)
{
int a, b, c;
cin >> n >> m >> st;
init();
for(int i = 0; i < m; ++i) {
cin >> a >> b >> c;
PUSH(a, b, c);
}
Dijk();
for (int i = 1; i <= n; i++)
cout << (dis[i] == INF ? INT_MAX : dis[i]) << " ";
cout << endl;
return 0;
}
P1629 邮递员送信
先正向求和从邮局到其他所有地点的距离,再将ma矩阵对调,再反向求一次从邮局到其他所有地点的距离
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f, N = 1005;
int ma[N][N];
int dis[N];
bool vis[N];
int n, m, st, ed;
void init()
{
// 初始化ma数组
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
ma[i][j] = (i == j ? 0 : INF);
}
void Dijk()
{
for (int i = 0; i < n - 1; i++) {
int minv = INF, k; // k用来存储最小的且没被松弛过的城市
for (int j = 1; j <= n; j++) {
if (minv > dis[j] && !vis[j]) {
minv = dis[j];
k = j;
}
}
vis[k] = 1;
for (int h = 1; h <= n; h++)
dis[h] = min(dis[h], dis[k] + ma[k][h]);
}
}
int main(void)
{
int a, b, c;
cin >> n >> m;
// 初始化ma数组
init();
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
if (ma[a][b] > c)
ma[a][b] = c;
}
// 输入起点和终点
st = 1;
// 初始化dis数组
for (int i = 1; i <= n; i++)
dis[i] = ma[st][i];
// 初始化vis数组
memset(vis, 0, sizeof vis);
vis[st] = 1;
Dijk();
int res = 0;
for (int i = 2; i <= n; i++) {
res += dis[i];
}
//cout << res << endl;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
swap(ma[i][j], ma[j][i]);
}
}
// 初始化dis数组
for (int i = 1; i <= n; i++)
dis[i] = ma[st][i];
// 初始化vis数组
memset(vis, 0, sizeof vis);
vis[st] = 1;
Dijk();
for (int i = 2; i <= n; i++) {
res += dis[i];
}
cout << res << endl;
return 0;
}
P4779【模板】单源最短路径(标准版)
这道题和P3371的区别在于数据最大为1e5
//邻接表写法
#include <bits/stdc++.h>
#define PUSH(x,y,z) G[x].push_back(P(y,z)) // 宏函数
using namespace std;
const int INF = 0x3f3f3f3f, N = 100005;
typedef pair<int, int> P;
int n, m, st, ed;
vector<P> G[N];
int dis[N];
bool vis[N];
void init()
{
for(int i = 0; i < N; i++)
G[i].clear();
memset(dis, INF, sizeof dis);
memset(vis, false, sizeof vis);
}
void Dijk()
{
dis[st] = 0;
priority_queue<P, vector<P>, greater<P> > que;
que.push(P(0, st));
while(que.size()) {
P p = que.top(); que.pop();
int u = p.second;
if(vis[u])
continue;
vis[u] = true;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].first;
int cost = G[u][i].second;
if(!vis[v] && dis[v] > dis[u] + cost) {
dis[v] = dis[u] + cost;
que.push(P(dis[v], v));
}
}
}
}
int main(void)
{
int a, b, c;
cin >> n >> m >> st;
init();
for(int i = 0; i < m; ++i) {
cin >> a >> b >> c;
PUSH(a, b, c);
}
Dijk();
for (int i = 1; i <= n; i++)
cout << (dis[i] == INF ? INT_MAX : dis[i]) << " ";
cout << endl;
return 0;
}
P1144 最短路计数
这道题的数据量是1e6,dijkatra+堆优化可以过
用一个ans数组来记录到达每个点的最短路条数
1、如果能够通过点u来进行优化到点v的最短路,就更新ans[v] = ans[u]
2、如果dis[v] == dis[u] + 1,就更新ans[v] = (ans[v] + ans[u]) % M
一篇很好的题解:https://www.luogu.com.cn/blog/Otto-Apocalypse/solution-p1144
//邻接表写法
#include <bits/stdc++.h>
#define PUSH(x,y,z) G[x].push_back(P(y,z)) // 宏函数
using namespace std;
const int INF = 0x3f3f3f3f, N = 1e6 + 5, M = 100003;
typedef pair<int, int> P;
int n, m, st, ed;
vector<P> G[N];
int dis[N], ans[N];
bool vis[N];
void init()
{
for(int i = 0; i < N; i++)
G[i].clear();
memset(dis, INF, sizeof dis);
memset(vis, false, sizeof vis);
}
void Dijk()
{
dis[st] = 0; ans[st] = 1;
priority_queue<P, vector<P>, greater<P> > que;
que.push(P(0, st));
while (que.size()) {
P p = que.top(); que.pop();
int u = p.second;
if (vis[u])
continue;
vis[u] = true;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].first;
int cost = G[u][i].second;
if(!vis[v] && dis[v] > dis[u] + cost) {
dis[v] = dis[u] + cost;
ans[v] = ans[u];
que.push(P(dis[v], v));
} else if (dis[v] == dis[u] + cost) {
ans[v] = (ans[v] + ans[u]) % M;
}
}
}
}
int main(void)
{
int a, b, c;
scanf("%d%d", &n, &m);
init();
for(int i = 0; i < m; ++i) {
scanf("%d%d", &a, &b);
PUSH(a, b, 1);
PUSH(b, a, 1);
}
st = 1;
Dijk();
for (int i = 1; i <= n; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
P1522 [USACO2.4]牛的旅行 Cow Tours
有两个牧场,求合并后牧场的最小直径是多少
先求出两个连通牧场的每个点的最大距离(两个牧场的直径存在于其中)。再分别枚举位于不同牧场的两个点,假设对这两个点进行连边,找出最小距离。
因为两个牧场连通成一个牧场之后的直径可能不如之前的直径大,所以需要从新牧场的直径的最小值和之前两个牧场的直径中选出连通之后的最小直径。
一份很好的题解:https://www.luogu.com.cn/problem/solution/P1522
#include <bits/stdc++.h>
using namespace std;
const int N = 155, INF = 0x3f3f3f3f;
double ma[N][N], max_dis[N]; // dis存储每个点到其他点的最大距离
double x[N], y[N]; // 坐标
int n;
// 计算两个牧区之间的距离
double cal(int i, int j)
{
return sqrt(pow(x[i] - x[j], 2) + pow(y[i] - y[j], 2));
}
void Floyd()
{
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
ma[i][j] = min(ma[i][j], ma[i][k] + ma[k][j]);
}
int main(void)
{
cin >> n;
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i];
}
char c;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> c;
if (i == j) {
ma[i][j] = 0;
} else if (c == '1') {
ma[i][j] = cal(i, j);
} else {
ma[i][j] = INF;
}
}
}
Floyd();
// 两个牧场连通前的最大直径
double maxv = 0;
// 计算 max_dis 和 maxv
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (ma[i][j] != INF)
max_dis[i] = max(ma[i][j], max_dis[i]);
}
maxv = max(maxv, max_dis[i]);
}
// 连通后牧场的最小直径
double minv = INF;
// 枚举两个不连通的牧区,寻找连通后的最小直径
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (ma[i][j] == INF) {
// 假设连通(i,j),寻找联通后最小的直径
minv = min(minv, max_dis[i] + cal(i, j) + max_dis[j]);
}
}
}
// 连通后的牧场可能比之前小
double res = max(maxv, minv);
printf("%.6f\n", res);
return 0;
}