洛谷 P4126 [AHOI2009]最小割
A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i(1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。
现在B国想找出一个路径切断方案,使中转站s不能到达中转站t,并且切断路径的代价之和最小。
小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题:
- 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
- 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?
现在请你回答这两个问题。
最小割的可行边和必须边
然而做这道题的时候并不会这东西QAQ
首先我们考虑(u,v)是可行边需要满足的条件
- 满流
- 在残余网络u和v不连通
满流就不用说了,如果u和v连通的话那么这个图也是连通的,就不是最小割了
然后考虑(u,v)是必须边需要满足的条件
- 满流
- 在残余网络中u和起点s连通,v和终点t连通
我们思考第二个条件,如果u和s不连通或者v和t连通,那么在单独的路径上一定会存在一条可行边,这个割掉和(u,v)割掉是等效的,就不是必须边了
所以我们可以直接对残余网络做tarjan,可行边需要满足u和v不在同一个强连通分量,必须边满足u和s在同一个强连通分量,v和t在同一个强连通分量
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 4000;
const int M = 6e4;
const int inf = 1e9;
using namespace std;
int id[M + 5],co_cnt,co[M + 5],n,m,s,t,dep[M + 5],cur[M + 5],head[M + 5],nxt[M * 2 + 5],q[M + 5],ans,edge_cnt = 1,U[M + 5],V[M + 5],dfn[M + 5],dfn_cnt,low[M + 5],stk[M + 5],top;
struct edges
{
int to,cost;
}edge[M * 2 + 5];
int dfs(int u,int flow)
{
if (u == t)
return flow;
int sum = 0;
for (int &i = cur[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (dep[v] == dep[u] + 1 && w)
{
int res = dfs(v,min(flow,w));
edge[i].cost -= res;
edge[i ^ 1].cost += res;
sum += res;
flow -= res;
if (!flow)
return sum;
}
}
if (!sum)
dep[u] = 0;
return sum;
}
int bfs()
{
for (int i = 1;i <= n;i++)
dep[i] = 0,cur[i] = head[i];
dep[s] = 1;
int l = 1,r = 0;
q[++r] = s;
while (l <= r)
{
int u = q[l++];
if (u == t)
return 1;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!dep[v] && w)
{
dep[v] = dep[u] + 1;
q[++r] = v;
}
}
}
return 0;
}
void add_edge(int u,int v,int w)
{
edge[++edge_cnt] = (edges){v,w};
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++dfn_cnt;
stk[++top] = u;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!w)
continue;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else
if (!co[v])
low[u] = min(low[u],dfn[v]);
}
if (low[u] == dfn[u])
{
co[u] = ++co_cnt;
while (stk[top] != u)
co[stk[top--]] = co_cnt;
top--;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
int w;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d",&U[i],&V[i],&w);
add_edge(U[i],V[i],w);
id[i] = edge_cnt;
add_edge(V[i],U[i],0);
}
while (bfs())
ans += dfs(s,inf);
for (int i = 1;i <= n;i++)
if (!dfn[i])
tarjan(i);
for (int i = 1;i <= m;i++)
{
if (!edge[id[i]].cost && co[U[i]] != co[V[i]])
printf("1 ");
else
printf("0 ");
if (!edge[id[i]].cost && co[U[i]] == co[s] && co[V[i]] == co[t])
printf("1 ");
else
printf("0 ");
putchar(10);
}
return 0;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 从零开始开发一个 MCP Server!
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档