2189. 有源汇上下界最大流

题目链接

2189. 有源汇上下界最大流

给定一个包含 \(n\) 个点 \(m\) 条边的有向图,每条边都有一个流量下界和流量上界。

给定源点 \(S\) 和汇点 \(T\),求源点到汇点的最大流。

输入格式

第一行包含四个整数 \(n,m,S,T\)

接下来 \(m\) 行,每行包含四个整数 \(a,b,c,d\) 表示点 \(a\)\(b\) 之间存在一条有向边,该边的流量下界为 \(c\),流量上界为 \(d\)

点编号从 \(1\)\(n\)

输出格式

输出一个整数表示最大流。

如果无解,则输出 No Solution

数据范围

\(1 \le n \le 202\),
\(1 \le m \le 9999\),
\(1 \le a,b \le n\),
\(0 \le c \le d \le 10^5\)

输入样例:

10 15 9 10
9 1 17 18
9 2 12 13
9 3 11 12
1 5 3 4
1 6 6 7
1 7 7 8
2 5 9 10
2 6 2 3
2 7 0 1
3 5 3 4
3 6 1 2
3 7 6 7
5 10 16 17
6 10 10 11
7 10 14 15

输出样例:

43

解题思路

最大流,有源汇上下界最大流

不妨先找有源汇上下界可行流,同无源汇上下界可行流一样,可以先建立两个虚拟节点 \(S,T\),多减的多加的可以通过虚拟节点补偿,且达到满流(即最大流)时说明找到一个可行流,但原图有源汇 \(s,t\),这样的 \(s,t\) 两个节点流量不守恒,但可以从 \(t\)\(s\) 建立一条容量足够大的边,这样两点的流量就能守恒,\(\color{red}{但 t 到 s这条边忽略掉后的可行流是否合理?}\)忽略这条边后,流网络的所有边都满足上下界要求,所以这时忽略这条边仍然是流网络的一个可行流,而且由 \(s\) 点的流量守恒,此时 \(t\)\(s\) 的流量正好等于残余网络的 \(s\)\(t\) 的可行流加上所有需要补充的下界,其数值等于原流网络的可行流
现在求解有源汇上下界最大流,一个可能可行的操作即先删除 \(t\)\(s\) 的边然后在原残留网络的基础上跑一遍 \(s\)\(t\) 的最大流,即将 \(s\)\(t\) 的残余网络完全榨干,但是需要是 \(s\)\(t\) 的残余网络才行,而现在这个网络却是 \(S\)\(T\) 的残余网络,残余网络不同可行流不能直接相加减,但原流网络的一个可行流对应现流网络下满流的 \(s\)\(t\) 的可行流,即所有与 \(S\)\(T\) 相关的边都满流了,在满流状态下,要求 \(s\)\(t\) 达到最大流,而在现残余网络中求解 \(s\)\(t\) 的最大流时并不会退流或引流到 \(S\)\(T\),即榨干 \(s\)\(t\) 的流量后 \(S\)\(T\) 相关的边仍然满流,此时对应原流网络中 \(s\)\(t\) 的最大流,\(\color{red}{为什么在现残余网络中求解 s 到 t 的最大流需要删除 t 到 s 的边?}\)现在的 \(t\)\(s\) 的边在原流网络中是不存在的,求解最大流时可能会有一部分流量在这条边上,所以需要删去,保证所求解的最大流在要求的边上

  • 时间复杂度:\(O(n^2m)\)

代码

// Problem: 有源汇上下界最大流
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2191/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=205,M=(N+10000)*2,inf=1e9;
int n,m,S,T,s,t,C[N];
int h[N],e[M],ne[M],f[M],idx;
int d[N],cur[N],q[N],hh,tt;
void add(int a,int b,int c)
{
	e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
	e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
bool bfs()
{
    memset(d,-1,sizeof d);
    q[0]=S;
    cur[S]=h[S];
    tt=hh=d[S]=0;
    while(hh<=tt)
    {
        int x=q[hh++];
        for(int i=h[x];~i;i=ne[i])
        {
            int y=e[i];
            if(d[y]==-1&&f[i])
            {
                d[y]=d[x]+1;
                cur[y]=h[y];
                if(y==T)return true;
                q[++tt]=y;
            }
        }
    }
    return false;
}
int dfs(int x,int limit)
{
    if(x==T)return limit;
    int flow=0;
    for(int i=cur[x];~i&&flow<limit;i=ne[i])
    {
        cur[x]=i;
        int y=e[i];
        if(d[y]==d[x]+1&&f[i])
        {
            int t=dfs(y,min(f[i],limit-flow));
            if(!t)d[y]=-1;
            f[i]-=t,f[i^1]+=t,flow+=t;
        }
    }
    return flow;
}
int dinic()
{
    int flow=0,res=0;
    while(bfs())while(flow=dfs(S,inf))res+=flow;
    return res;
}
int main()
{
	memset(h,-1,sizeof h);
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
    	int a,b,c,d;
    	scanf("%d%d%d%d",&a,&b,&c,&d);
    	add(a,b,d-c);
    	C[a]-=c,C[b]+=c;
    }
    S=0,T=n+1;
    int tot=0;
    for(int i=1;i<=n;i++)
    	if(C[i]>0)tot+=C[i],add(S,i,C[i]);
    	else if(C[i]<0)
    		add(i,T,-C[i]);
    add(t,s,inf);
    if(tot!=dinic())puts("No Solution");
    else
    {
    	int res=f[idx-1];
    	f[idx-1]=f[idx-2]=0;
    	S=s,T=t;
    	printf("%d\n",res+dinic());
    }
    return 0;
}
posted @ 2022-11-25 16:09  zyy2001  阅读(92)  评论(0编辑  收藏  举报