编写一个程序exp8-16.cpp,用来求解最短路径问题,假设有n个点,m条无向边,每条边都有长度d和花费p
时间: 2023-11-18 22:02:09 浏览: 125
以下是exp8-16.cpp的代码:
```c++
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
const int MAXM = 20005;
const int INF = 0x3f3f3f3f;
int n, m, s, t;
int head[MAXN], nxt[MAXM], to[MAXM], w[MAXM], c[MAXM], cnt;
int dis[MAXN], cost[MAXN], pre[MAXN], vis[MAXN];
void add_edge(int u, int v, int d, int p) {
nxt[++cnt] = head[u];
to[cnt] = v;
w[cnt] = d;
c[cnt] = p;
head[u] = cnt;
}
bool spfa() {
memset(dis, INF, sizeof(dis));
memset(cost, INF, sizeof(cost));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
cost[s] = 0;
vis[s] = true;
while (true) {
bool flag = false;
for (int i = head[s]; i; i = nxt[i]) {
int v = to[i];
if (dis[v] > dis[s] + w[i] && c[i] > 0) {
dis[v] = dis[s] + w[i];
cost[v] = cost[s] + c[i];
pre[v] = i;
if (!vis[v]) {
vis[v] = true;
flag = true;
}
}
}
for (int u = 1; u <= n; u++) {
if (u != s && vis[u]) {
vis[u] = false;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (dis[v] > dis[u] + w[i] && c[i] > 0) {
dis[v] = dis[u] + w[i];
cost[v] = cost[u] + c[i];
pre[v] = i;
if (!vis[v]) {
vis[v] = true;
flag = true;
}
}
}
}
}
if (!flag) {
break;
}
}
return pre[t] != -1;
}
int main() {
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++) {
int u, v, d, p;
cin >> u >> v >> d >> p;
add_edge(u, v, d, p);
add_edge(v, u, d, p);
}
int ans1 = 0, ans2 = 0;
while (spfa()) {
int flow = INF;
for (int i = t; i != s; i = to[pre[i] ^ 1]) {
flow = min(flow, c[pre[i]]);
}
for (int i = t; i != s; i = to[pre[i] ^ 1]) {
c[pre[i]] -= flow;
c[pre[i] ^ 1] += flow;
}
ans1 += flow;
ans2 += flow * cost[t];
}
cout << ans1 << " " << ans2 << endl;
return 0;
}
```
代码解释:
- 第1行到第3行定义了常量MAXN、MAXM和INF,其中MAXN表示最大的点数,MAXM表示最大的边数,INF表示无穷大。
- 第5行到第7行定义了变量n、m、s和t,其中n表示点数,m表示边数,s表示起点,t表示终点。
- 第9行到第19行定义了邻接表存图所需要的变量和函数。变量head数组表示每个点的第一条边,nxt数组表示每条边的下一条边,to数组表示每条边的另一个端点,w数组表示每条边的长度,c数组表示每条边的花费,cnt表示边的数量。函数add_edge用来添加一条从u到v长度为d、花费为p的边。
- 第21行到第50行是SPFA算法的实现。变量dis数组表示从起点s到每个点的最短路径长度,cost数组表示从起点s到每个点的最小花费,pre数组表示每个点在最短路径上的前驱边,vis数组表示每个点是否在队列中。在SPFA算法中,我们先将dis、cost、pre和vis数组初始化,然后将起点s加入队列中。接下来,我们不断从队列中取出元素,更新其可达的点的dis、cost、pre和vis数组。具体来说,我们对于每个点u,遍历其所有相邻的点v,如果dis[v]>dis[u]+w[i]且c[i]>0,则更新dis[v]、cost[v]、pre[v]和vis[v],并将v加入队列中。然后,我们再次遍历所有点u,如果u在队列中且vis[u]为true,则遍历它所有相邻的点v,进行同样的更新操作。如果队列为空,则SPFA算法结束。最后,我们返回pre[t]!=-1判断是否存在从起点s到终点t的路径。
- 第52行到第56行是主函数。我们先读入n、m、s和t,并使用add_edge函数添加所有边。然后,我们使用最小费用最大流算法求解最短路径问题。具体来说,我们不断使用spfa函数找到从起点s到终点t的最短路径,然后在这条路径上找到最小剩余容量flow,将这个flow加入最大流答案ans1中,将flow乘以路径总花费加入最小费用ans2中,然后更新每条边的剩余容量和反向边的容量。如果不存在从起点s到终点t的路径,则最小费用最大流算法结束。最后,我们输出ans1和ans2即可。
注意:本程序中使用了SPFA算法和最小费用最大流算法,需要读者具备相关知识。
阅读全文