LOJ模板题 无源汇有上下界可行流

来源:互联网 发布:电信4g网络覆盖范围 编辑:程序博客网 时间:2024/06/06 07:18

https://loj.ac/problem/115

没有loj我都不知道有这种操作。

如何求得一个无源汇的图是否可行呢,是一个网络流可以解决的问题。

首先,我们知道每条边的起点u,终点v,下界low,上界up。最初始的图理应是由各条边的下界流量组成的。

然后我们看一看每个点是否能做到流入量和流出量平衡。本来就平衡的话就不用管这个点了。如果不平衡,我们分情况讨论

如果对于点p,流入量比流出量多x,那么我们建立一条超级源点ss到p流量上界为x的边。

反之,建立一条p到超级汇点tt的流量上界为x的边。

此时跑一遍网络流,如果与超级源点相连的边都满流了,也就说明超级汇点也满流了,整个图是可行的。否则是不可行的。

若还想知道每条边在可行方案中的流量是多少,我们可以用这条边的下界流量加上跑完网络流后这条边的反边的流量即可。

比如loj上的样例

4 61 2 1 32 3 1 33 4 1 34 1 1 31 3 1 34 2 1 3

一开始建立的图:


我们可以从每条边的下界先分析出某些点流入和流出不相等,按照上面的方法,我们建立超级源点和超级汇点。得到


整个图已经建立起来了,上面的数字都是当前边最大的可行流量(原来的边的流量就是up-low),当前流量是为0的。跑一遍网络流可得


那么可以看到和超级源点所连的边都是满流的,所以有可行方案。每条边的流量就是每条边的下界加上跑完网络流后当前边的流量。

代码如下:

#include<iostream>#include<cstring>#include<cstdio>#include<queue>const int maxn = 205;const int maxm = 10500;using namespace std;inline int read(){int x=0,t=1,c;while(!isdigit(c=getchar()))if(c=='-')t=-1;while(isdigit(c))x=x*10+c-'0',c=getchar();return x*t;}int head[maxn],cur[maxn],nx[maxm<<1],to[maxm<<1],flow[maxm<<1],ppp=2;struct Dinic{int d[maxn];int s,t;long long ans;void AddEdge(int u,int v,int c){to[ppp]=v;flow[ppp]=c;nx[ppp]=head[u];head[u]=ppp++;swap(u,v);to[ppp]=v;flow[ppp]=0;nx[ppp]=head[u];head[u]=ppp++;}bool BFS(){memset(d,-1,sizeof d);d[t]=1;queue<int> Q;Q.push(t);while(!Q.empty()){int x=Q.front();Q.pop();for(int i=head[x];i;i=nx[i]){if(flow[i^1]&&d[to[i]]==-1){d[to[i]]=d[x]+1;Q.push(to[i]);}}}return d[s]!=-1;}int DFS(int x,int maxflow){if(x==t||!maxflow){ans+=maxflow;return maxflow;}int ret=0,f;for(int &i=cur[x];i;i=nx[i]){if(d[to[i]]==d[x]-1&&(f=DFS(to[i],min(maxflow,flow[i])))){ret+=f;flow[i]-=f;flow[i^1]+=f;maxflow-=f;if(!maxflow)break;}}return ret;}long long solve(int source,int tank){s=source;t=tank;ans=0;while(BFS()){memcpy(cur,head,sizeof(cur));DFS(s,2147483647);}return ans;}}dinic;int ru[maxn], low[maxm], sum;int main(){int n=read(),m=read();for(int i = 0; i < m; i++){int u=read(),v=read();low[i] = read();int up = read();dinic.AddEdge(u,v,up - low[i]);ru[u] -= low[i];ru[v] += low[i];}for(int i = 1; i <= n; i++) {if(ru[i] > 0) {dinic.AddEdge(0, i, ru[i]);sum += ru[i];} else if(ru[i] < 0) {dinic.AddEdge(i, n + 1, -ru[i]);}}int tmp = dinic.solve(0,n + 1);if(tmp == sum) {cout << "YES" << '\n';for(int i = 0; i < m; i++) {printf("%d\n", low[i] + flow[2 + i * 2 + 1]);}} else {cout << "NO" << '\n';}return 0;}