最短路径常用算法有Dijkstra和SPFA。SPFA支持负数权重,但容易被毒瘤数据卡。
想让SPFA跑快点可以加入一个小优化:用deque代替queue,然后在Push的时候分类,如果比当前front的dis要小就Push Front,否则Push Back。
最短路计数和次短路问题仿照普通DP处理即可。次短路中严格非严格注意区别对待。
建模方面主要考虑分层图等常见技巧。处理实际问题时可能结合二分答案或者枚举进行。
10074. 「一本通 3.2 例 3」架设电话线
#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP { // 解决单源最短路径问题
public:
vector<pair<int, T> > G[MAX_NODE];
int d[MAX_NODE], v[MAX_NODE], f[MAX_NODE];
T t[MAX_NODE]; // 距离表与访问标识表
void make(int t1, int t2, T t3) { // 造边(有向)
G[t1].push_back(make_pair(t2, t3));
}
void reset_graph(int n) { // 用于清除图邻接表
for (int i = 0; i <= n; i++) G[i].clear();
}
void reset_solver(int n) { // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
memset(d, 0x3f, sizeof d);
memset(v, 0x00, sizeof v);
memset(f, 0x00, sizeof f);
}
void solveSPFA(int v0, int n) { // 执行主计算任务(使用SPFA)
queue<int> q;
reset_solver(n); // 自动调用对距离表与访问标识表的清除
// d[0]=0;
d[v0] = 0;
v[v0] = 1;
q.push(v0);
while (q.size()) {
int p = q.front();
q.pop();
v[p] = 0;
for (int i = 0; i < G[p].size(); i++) {
int x = G[p][i].first; // x为当前枚举边的终点,
T y = G[p][i].second; // y为当前枚举边的权值
if (d[x] > d[p] + y) {
d[x] = d[p] + y;
t[x] = y;
f[x] = p;
if (!v[x])
q.push(x);
}
}
}
}
};
Graph_SP<int> g;
int n, m, k;
struct Edge {
int u, v, w;
bool operator<(const Edge &b) { return w < b.w; }
} e[MAX_EDGE];
int check(int lim) {
g.reset_graph(n);
for (int i = 1; i <= m; i++) {
g.make(e[i].u, e[i].v, (e[i].w > lim));
g.make(e[i].v, e[i].u, (e[i].w > lim));
}
g.solveSPFA(1, n);
return g.d[n];
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[i].u = u;
e[i].v = v;
e[i].w = w;
}
sort(e + 1, e + m + 1);
int l = 0, r = 1e+9;
while (r > l) {
int mid = (l + r) >> 1;
if (check(mid) > k)
l = mid + 1;
else
r = mid;
}
cout << (l >= 1e+9 ? -1 : l) << endl;
}
10075. 「一本通 3.2 练习 1」农场派对
#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 200005;
template <class T>
class Graph_SP { // 解决单源最短路径问题
public:
vector<pair<int, T> > G[MAX_NODE];
int d[MAX_NODE], v[MAX_NODE]; // 距离表与访问标识表
void make_edge(int t1, int t2, T t3) { // 造边(有向)
G[t1].push_back(make_pair(t2, t3));
}
void reset_graph(int n) { // 用于清除图邻接表
for (int i = 0; i <= n; i++) G[i].clear();
}
void reset_solver(int n) { // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
memset(d, 0x3f, sizeof d);
memset(v, 0x00, sizeof v);
}
void solveSPFA(int v0, int n) { // 执行主计算任务(使用SPFA)
queue<int> q;
reset_solver(n); // 自动调用对距离表与访问标识表的清除
d[v0] = 0;
v[v0] = 1;
q.push(v0);
while (q.size()) {
int p = q.front();
q.pop();
v[p] = 0;
for (int i = 0; i < G[p].size(); i++) {
int x = G[p][i].first; // x为当前枚举边的终点,
T y = G[p][i].second; // y为当前枚举边的权值
if (d[x] > d[p] + y) {
d[x] = d[p] + y;
if (!v[x])
q.push(x);
}
}
}
}
};
Graph_SP<int> g1, g2;
int n, m, x, u, v, w;
int main() {
cin >> n >> m >> x;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
g1.make_edge(u, v, w);
g2.make_edge(v, u, w);
}
g1.solveSPFA(x, n);
g2.solveSPFA(x, n);
int ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, g1.d[i] + g2.d[i]);
}
cout << ans << endl;
}
10076. 「一本通 3.2 练习 2」Roadblocks
#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 5005;
const int MAX_EDGE = 200005;
template <class T>
class Graph_SP { // 解决单源最短路径问题
public:
vector<pair<int, T> > G[MAX_NODE];
int d[MAX_NODE], v[MAX_NODE], s[MAX_NODE]; // 距离表与访问标识表
void make_edge(int t1, int t2, T t3) { // 造边(有向)
G[t1].push_back(make_pair(t2, t3));
}
void reset_graph(int n) { // 用于清除图邻接表
for (int i = 0; i <= n; i++) G[i].clear();
}
void reset_solver(int n) { // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
memset(d, 0x3f, sizeof d);
memset(s, 0x3f, sizeof d);
memset(v, 0x00, sizeof v);
}
void solveSPFA(int v0, int n) { // 执行主计算任务(使用SPFA)
queue<int> q;
reset_solver(n); // 自动调用对距离表与访问标识表的清除
d[v0] = 0;
v[v0] = 1;
q.push(v0);
while (q.size()) {
int p = q.front();
q.pop();
v[p] = 0;
for (int i = 0; i < G[p].size(); i++) {
int x = G[p][i].first; // x为当前枚举边的终点,
T y = G[p][i].second; // y为当前枚举边的权值
if (d[x] > d[p] + y) {
s[x] = d[x];
d[x] = d[p] + y;
if (!v[x])
q.push(x), v[x] = 1;
}
if (s[x] > d[p] + y && d[p] + y != d[x]) {
s[x] = d[p] + y;
if (!v[x])
q.push(x), v[x] = 1;
}
if (s[x] > s[p] + y && s[p] + y != d[x]) {
s[x] = s[p] + y;
if (!v[x])
q.push(x), v[x] = 1;
}
}
}
}
};
Graph_SP<int> g;
int n, m, u, v, w;
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
g.make_edge(u, v, w);
g.make_edge(v, u, w);
}
g.solveSPFA(1, n);
cout << g.s[n] << endl;
}
10078. 「一本通 3.2 练习 4」新年好
#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP { // 解决单源最短路径问题
public:
vector<pair<int, T> > G[MAX_NODE];
int d[MAX_NODE], v[MAX_NODE]; // 距离表与访问标识表
void make_edge(int t1, int t2, T t3) { // 造边(有向)
G[t1].push_back(make_pair(t2, t3));
}
void reset_graph(int n) { // 用于清除图邻接表
for (int i = 0; i <= n; i++) G[i].clear();
}
void reset_solver(int n) { // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
memset(d, 0x3f, sizeof d);
memset(v, 0x00, sizeof v);
}
void solveSPFA(int v0, int n) { // 执行主计算任务(使用SPFA)
queue<int> q;
reset_solver(n); // 自动调用对距离表与访问标识表的清除
d[v0] = 0;
v[v0] = 1;
q.push(v0);
while (q.size()) {
int p = q.front();
q.pop();
v[p] = 0;
for (int i = 0; i < G[p].size(); i++) {
int x = G[p][i].first; // x为当前枚举边的终点,
T y = G[p][i].second; // y为当前枚举边的权值
if (d[x] > d[p] + y) {
d[x] = d[p] + y;
if (!v[x])
q.push(x), v[x] = 1;
}
}
}
}
};
Graph_SP<int> g[6];
int n, m, p[6], u, v, w;
int perm[6] = { 0, 1, 2, 3, 4, 5 };
int main() {
cin >> n >> m >> p[1] >> p[2] >> p[3] >> p[4] >> p[5];
p[0] = 1;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
for (int j = 0; j < 6; j++) g[j].make_edge(u, v, w), g[j].make_edge(v, u, w);
}
for (int i = 0; i < 6; i++) {
g[i].solveSPFA(p[i], n);
}
int ans = 1e+9;
while (next_permutation(perm + 1, perm + 6)) {
int sum = 0;
for (int i = 1; i < 6; i++) sum += g[perm[i - 1]].d[p[perm[i]]];
ans = min(ans, sum);
}
cout << ans << endl;
}
10081. 「一本通 3.2 练习 7」道路和航线
#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP { // 解决单源最短路径问题
public:
vector<pair<int, T> > G[MAX_NODE];
int d[MAX_NODE], v[MAX_NODE]; // 距离表与访问标识表
void make_edge(int t1, int t2, T t3) { // 造边(有向)
G[t1].push_back(make_pair(t2, t3));
}
void reset_graph(int n) { // 用于清除图邻接表
for (int i = 0; i <= n; i++) G[i].clear();
}
void reset_solver(int n) { // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
memset(d, 0x3f, sizeof d);
memset(v, 0x00, sizeof v);
}
void solveSPFA(int v0, int n) { // 执行主计算任务(使用SPFA)
deque<int> q;
reset_solver(n); // 自动调用对距离表与访问标识表的清除
d[v0] = 0;
v[v0] = 1;
q.push_front(v0);
while (q.size()) {
int p = q.front();
q.pop_front();
v[p] = 0;
for (int i = 0; i < G[p].size(); i++) {
int x = G[p][i].first; // x为当前枚举边的终点,
T y = G[p][i].second; // y为当前枚举边的权值
if (d[x] > d[p] + y) {
d[x] = d[p] + y;
if (!v[x]) {
v[x] = 1;
if (!q.empty() && d[x] < d[q.front()])
q.push_front(x);
else
q.push_back(x);
}
}
}
}
}
};
int n, m, p, s, u, v, w;
Graph_SP<int> g;
int main() {
ios::sync_with_stdio(false);
cin >> n >> m >> p >> s;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
g.make_edge(u, v, w);
g.make_edge(v, u, w);
}
for (int i = 1; i <= p; i++) {
cin >> u >> v >> w;
g.make_edge(u, v, w);
}
g.solveSPFA(s, n);
for (int i = 1; i <= n; i++) {
if (g.d[i] >= 0x3f3f3f3f)
cout << "NO PATH" << endl;
else
cout << g.d[i] << endl;
}
}