解题思路
使用费用流求解。首先把题中所有的边都按流量为 1,费用为路径长度加边,这样就保证了每条路只能走一次,且求出来的最小费用就是最短路径长度。之后再把源点到 1 以及 n 到汇点都加一条流量为 2,费用为 0 的边,这是为了支持题目中的往返操作。这样跑一边最小费用最大流即可求出答案。
参考代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1002;
const int MAXM = 20002;
struct edge {
int u, v, f, c;
int next;
} e[MAXM<<1];
int cnt, head[MAXN];
bool vis[MAXN];
int dis[MAXN], pre[MAXN];
void Init() {
cnt = 0;
memset(head, -1, sizeof head);
}
void AddEdge(int u, int v, int f, int c) {
e[cnt].u = u;
e[cnt].v = v;
e[cnt].f = f;
e[cnt].c = c;
e[cnt].next = head[u];
head[u] = cnt++;
e[cnt].u = v;
e[cnt].v = u;
e[cnt].f = 0;
e[cnt].c = -c;
e[cnt].next = head[v];
head[v] = cnt++;
}
bool SPFA(int s, int t) {
queue<int>q;
q.push(s);
memset(dis, 0x3f, sizeof dis);
memset(pre, -1, sizeof pre);
memset(vis, false, sizeof vis);
dis[s] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u]; ~i; i=e[i].next) {
int v = e[i].v;
int f = e[i].f;
int c = e[i].c;
if(f>0 && dis[u]+c<dis[v]) {
dis[v] = dis[u] + c;
pre[v] = i;
if(!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1) return false;
else return true;
}
pair<int, int> MinCostMaxFlow(int s, int t) {
int max_flow = 0, min_cost = 0;
while(SPFA(s, t)) {
int flow = INF;
for(int i=pre[t]; ~i; i=pre[e[i].u]) {
flow = min(flow, e[i].f);
}
max_flow += flow;
min_cost += flow * dis[t];
for(int i=pre[t]; ~i; i=pre[e[i].u]) {
e[i].f -= flow;
e[i^1].f += flow;
}
}
return make_pair(max_flow, min_cost);
}
int main(int argc, char const *argv[]) {
int n, m, u, v, c;
scanf("%d %d", &n, &m);
Init();
while(m--) {
scanf("%d %d %d", &u, &v, &c);
AddEdge(u, v, 1, c);
AddEdge(v, u, 1, c);
}
AddEdge(0, 1, 2, 0);
AddEdge(n, n+1, 2, 0);
printf("%d\n", MinCostMaxFlow(0, n+1).second);
return 0;
}
还不快抢沙发