好久都没有搞博客了。想认真写又要准备文化课期末了。
ISAP
- 流程: 原理就是dfs找增广路。
最基础的建反向边以便反悔就不说了。
但是记录一个dep(dis)表示层数,一开始BFS(从t开始,dis[t]=0)处理最小层数,然后再搜索增广路增加限制条件:dis[u]=dis[v]+1,若这样的v找完了,扩大一层u(即dis[u]++),可能会被回溯到前面的某条新路再次搜中。然后特判一下,如果dis[s]>=n即可结束,因为dis[t]永远等于0,dis[s]最大为n-1。
gap(cnt)标记很简单:就是记录每个dis[]值的个数,若某一中间dis[]值为0,则出现了断层,理论上就搜不到增广路了。 - 代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
int head[N], cur[N], to[N], nxt[N], S, T, dis[N], gap[N], ecnt = 1, len[N], n, m;
void add_edge(int u, int v, int w) {
nxt[++ecnt] = head[u]; to[ecnt] = v; len[ecnt] = w; cur[u] = head[u] = ecnt;
nxt[++ecnt] = head[v]; to[ecnt] = u; len[ecnt] = 0; cur[v] = head[v] = ecnt;
}
void BFS() {
memset(dis, -1, sizeof(dis));
memset(gap, 0, sizeof(gap));
queue<int> Q;
Q.push(T);
gap[dis[T] = 0]++;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if(dis[v] != -1) continue;
gap[dis[v] = dis[u] + 1]++;
Q.push(v);
}
}
}
int dfs(int u, int flow) {
if(u == T) return flow;
int used = 0;
for(int i = cur[u]; i; i = nxt[i]) {
int v = to[i];
if(len[i] && dis[v] + 1 == dis[u]) {
int tmp = dfs(v, min(flow - used, len[i]));
if(tmp) {
len[i] -= tmp; len[i ^ 1] += tmp;
used += tmp;
if(used == flow) return flow;
}
}
cur[u] = i; //下次遍历到 u时, dis不变而前面的已经考虑过了。
}
if(!--gap[dis[u]++]) {dis[S] = n;}
gap[dis[u]]++;
cur[u] = head[u];
return used;
}
int ISAP() {
int mxflow = 0;
for(BFS(); dis[S] < n; mxflow += dfs(S, inf));
return mxflow;
}
signed main() {
scanf("%d%d%d%d", &n, &m, &S, &T);
for(int i = 1; i <= m; i++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
}
int ans = ISAP();
printf("%lld", ans);
return 0;
}
费用流算法
- 流程:
首先变化在于,找最大流的基础上满足最小(大)费用。其次,每条边上多了费用一变量,因此我们在存反边时边的费用为负的原费用
然后我们把EK_BFS找增广路改为找最短(长)路即可。注意记录前驱,最后结束时更新边信息。 - 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int mxflow=0,mnval=0,dis[N],head[N],n,m,s,t,to[N],nxt[N],len[N],val[N],ecnt=1,flow[N],pre[N],inf=0x3f3f3f3f;
void add_edge(int u,int v,int z,int w) {
nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=z;val[ecnt]=w;head[u]=ecnt;
nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=0;val[ecnt]=-w;head[v]=ecnt;
}
bool In_q[N];
queue<int> Q;
void init() {
for(int i=0;i<=n;i++)dis[i]=flow[i]=inf,In_q[i]=0,pre[i]=0;
}
bool spfa() {
init();
Q.push(s); dis[s]=0,In_q[s]=1;
while(!Q.empty()) {
int u=Q.front(); Q.pop(); In_q[s]=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(len[i]&&dis[v]>dis[u]+val[i]) {
dis[v]=dis[u]+val[i];
flow[v]=min(flow[u],len[i]);
pre[v]=i^1; //记录终点往前的边的编号
Q.push(v),In_q[v]=1;
}
}
}
if(dis[t]==0x3f3f3f3f) return false;
return true;
}
void mn_valflow() {
while(spfa()) {
mxflow+=flow[t];
mnval+=flow[t]*dis[t];
int k=t;
while(pre[k]) {
len[pre[k]]+=flow[t],len[pre[k]^1]-=flow[t];
k=to[pre[k]];
}
}
}
int main() {
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++) {
int u,v,z,w;
scanf("%d%d%d%d",&u,&v,&z,&w);
add_edge(u,v,z,w);
}
mn_valflow();
printf("%d %d",mxflow,mnval);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人