P3381 【模板】最小费用最大流
最大流算法中的DFS搜索修改为SPFA算法,每次取增广路径时,都取成本最低,即最近路径。
可以得73分,三组数据超时,可能是跑进了死循环(其实并不是,SPFA没错),没发现错误,还是跟着答案学学吧。
#include<iostream>
#include<algorithm>
#include<queue>
#include<memory.h>
using namespace std;
const int MAXN = 5e3 + 10, MAX = 5e5 + 10;
const int INF = 0x3f3f3f3f;
int cnt = 0;
struct link {
int v, w, c;
int next;
}edge[MAX];
int head[MAXN];
int getk(int u, int v) {
int k;
for (k = head[u]; k; k = edge[k].next) {
if (edge[k].v == v)return k;
}
return 0;
}
void addedge(int u, int v, int w, int c) {
edge[++cnt].v = v; edge[cnt].w = w; edge[cnt].c = c;
edge[cnt].next = head[u]; head[u] = cnt;
}
int n, m, s, t;
int pre[MAXN];
int dist[MAXN];
bool flag[MAXN];
int node[MAXN];
bool SPFA() {
memset(dist, 0x3f, sizeof(dist));
memset(flag, 0, sizeof(flag));
memset(pre, 0, sizeof(pre));
queue<int>q;
q.push(s);
node[s] = INF;
dist[s] = 0;
flag[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
flag[u] = 0;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v, c = edge[i].c, w = edge[i].w;
if (w&&dist[v] > dist[u] + c) {
dist[v] = dist[u] + c;
pre[v] = u;
node[v] = min(node[u], w);
if (!flag[v]) {
flag[v] = 1;
q.push(v);
}
}
}
}
if (dist[t] >= INF)return 0;
return 1;
}
void EK() {
int ans = 0, all = 0;
while (SPFA()) {
int v = t, u;
while (v != s) {
u = pre[v];
edge[getk(u, v)].w -= node[t];
addedge(v, u, node[t], -edge[getk(u, v)].c);
v = u;
}
ans += node[t];
all += node[t] * dist[t];
}
cout << ans << ' ' << all << endl;
}
int main() {
cin >> n >> m >> s >> t;
int u, v, w, c;
for (int i = 0; i < m; i++) {
cin >> u >> v >> w >> c;
addedge(u, v, w, c);
}
EK();
system("pause");
return 0;
}
为什么会超时呢?是静态链表更改时的问题。。。当时感觉很绕,但是没想到更好的解决办法。我们写的代码在更新边的剩余流量时,是通过记录两个端点,这就需要遍历一个链表去找到这个边的储存位置时间复杂度是 O ( n ) O(n) O(n)。洛谷中有三个数据会多次更改边的流量,这样就被卡了。
如何优化呢?在记录pre数组时,不在记录端点去寻找边的储存位置,而是直接储存下当前储存位置及数组下标。
另一个问题就是在减少一个边的流量时,需要增加这个边相反边的流量,即创造反向边,反向边如何定位呢?如果使用端点寻找时间复杂度是 O ( n ) O(n) O(n)。
这个需要我们在储存时就开始处理,我们在输入一个边时就,一同将其反向边建立,我们将一个边储存在数组下标2的位置,将其反向边储存在下标3的位置那么我们对其中的一个边做 i 1 i^1 i1运算我们就得到了另一条。如此储存所有的边与反向边,这样我们就可以通过一个边位置求出其反向边的位置。
#include<iostream>
#include<algorithm>
#include<queue>
#include<memory>
using namespace std;
const int MAXN = 5e3 + 10, MAX = 5e5 + 10;
const int INF = 0x3f3f3f3f;
int cnt = 1;
struct link {
int v, w, c;
int next;
}edge[MAX];
int head[MAXN];
void addedge(int u, int v, int w, int c) {
edge[++cnt].v = v; edge[cnt].w = w; edge[cnt].c = c;
edge[cnt].next = head[u]; head[u] = cnt;
}
int n, m, s, t;
int pre[MAXN];
int dist[MAXN];
bool flag[MAXN];
int node[MAXN];
bool SPFA() {
memset(dist, 0x3f, sizeof(dist));
memset(flag, 0, sizeof(flag));
memset(pre, 0, sizeof(pre));
queue<int>q;
q.push(s);
node[s] = INF;
dist[s] = 0;
flag[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
flag[u] = 0;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v, c = edge[i].c, w = edge[i].w;
if (w&&dist[v] > dist[u] + c) {
dist[v] = dist[u] + c;
pre[v] = i;
node[v] = min(node[u], w);
if (!flag[v]) {
flag[v] = 1;
q.push(v);
}
}
}
}
if (dist[t] >= INF)return 0;
return 1;
}
void EK() {
int ans = 0, all = 0;
while (SPFA()) {
int v = t, i;
while (v != s) {
i = pre[v];
edge[i].w -= node[t];
edge[i ^ 1].w += node[t];
v = edge[i ^ 1].v;
}
ans += node[t];
all += node[t] * dist[t];
}
cout << ans << ' ' << all << endl;
}
int main() {
cin >> n >> m >> s >> t;
int u, v, w, c;
for (int i = 0; i < m; i++) {
cin >> u >> v >> w >> c;
addedge(u, v, w, c);
addedge(v, u, 0, -c);
}
EK();
system("pause");
return 0;
}