我们考虑普通的网络流只有上界,加个下界怎么做?
1.我们先把下界的限制消掉,就是使得每条边强制流上他下界的流量,这样限制得到了转化(只有上界),但是并不一定满足流量守恒
2.调整流量,使得他变成一个可行流,我们考虑建虚拟源汇点SS,TT,然后考虑如果某个点流量盈余a[i]<0就让他连一条向TT的容量上限为abs(a[i])的边,a[i]>0同理,然后我们跑一遍最大流,只要使得最大流为就保证原网络有可行流(由于该网络有源汇,所以要使T->S连一条容量上限为INF的边来保证每个点都满足流量守恒)
3.最后一步就是可行流转化为最大/最小流,我们只要在残量网络上跑一遍最大流就行了(求最大流),如果求最小流,只要T->S跑一遍最大流就行了
模板
void add(int x,int y,int l,int r) {
edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
}
void add_new(int x,int y,int z){
edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
}
bool bfs(int S,int T) {
queue<int>q;
q.push(S);
memset(arrange,0,sizeof(arrange));
arrange[S]=1;
while (!q.empty()) {
int v=q.front(); q.pop(); if (v==T) return 1;
for (int i=head[v];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (edge[i].w&&!arrange[u]) {
arrange[u]=arrange[v]+1;
q.push(u);
}
}
}
return 0;
}
int dfs(int now,int maxlow,int t) {
if (now==t) return maxlow;
int ret=0;
for (int &i=cur[now];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (arrange[u]==arrange[now]+1&&edge[i].w) {
int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
edge[i].w-=f; edge[i^1].w+=f;
if (ret==maxlow) return ret;
}
}
return ret;
}
int dinic(int S,int T) {
int ans=0;
while (bfs(S,T)) {
memcpy(cur,head,sizeof(head));
ans+=dfs(S,INF,T);
}
return ans;
}
//以上为最大流dinic
void upper_lower_maxflow(int S,int T,int n) {//n:总结点个数
add(T,S,0,INF);
for (int i=1;i<=n+2;i++) {
for (int j=head[i];j!=-1;j=edge[j].to)
{
ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
}
}//计算节点盈余,在这里S=n+1,T=n+2
int SS=n+3,TT=n+4;
int maxflow=0;
for (int i=1;i<=n+2;i++)
if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
if (maxflow!=dinic(SS,TT)) {
puts("JIONG!"); return;
}
maxflow=0;
for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0; //去掉S-T的边
dinic(T,S);//这里求最小流
for (int i=head[T];i!=-1;i=edge[i].to) {
maxflow+=edge[i].w+edge[i^1].l;
}
printf("%d\n",maxflow);
}
由于边可以重复,所以S向每个点连一条下界为0,上界为INF的边,每个点向T连一条下界为0,上界为INF的边,如果两点u->v在原图上有边,那么就连一条u->v下界为1,上界为INF的边,最后直接跑上下界网络流就行了
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N=300;
const int M=50010;
const int INF=0x3f3f3f3f;
struct node{
int u,to,l,r,w;
};
node edge[M<<1];
int arrange[N],head[N],a[N],cur[N],ret_flow[N];
int n,cnt,S,T,k,x,y;
void add(int x,int y,int l,int r) {
edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
}
void add_new(int x,int y,int z){
edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
}
bool bfs(int S,int T) {
queue<int>q;
q.push(S);
memset(arrange,0,sizeof(arrange));
arrange[S]=1;
while (!q.empty()) {
int v=q.front(); q.pop(); if (v==T) return 1;
for (int i=head[v];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (edge[i].w&&!arrange[u]) {
arrange[u]=arrange[v]+1;
q.push(u);
}
}
}
return 0;
}
int dfs(int now,int maxlow,int t) {
if (now==t) return maxlow;
int ret=0;
for (int &i=cur[now];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (arrange[u]==arrange[now]+1&&edge[i].w) {
int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
edge[i].w-=f; edge[i^1].w+=f;
if (ret==maxlow) return ret;
}
}
return ret;
}
int dinic(int S,int T) {
int ans=0;
while (bfs(S,T)) {
memcpy(cur,head,sizeof(head));
ans+=dfs(S,INF,T);
}
return ans;
}
void upper_lower_maxflow(int S,int T) {
for (int i=1;i<=n+2;i++) {
for (int j=head[i];j!=-1;j=edge[j].to)
{
ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
}
}
int SS=++cnt,TT=++cnt;
int maxflow=0;
for (int i=1;i<=n+2;i++)
if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
if (maxflow!=dinic(SS,TT)) {
printf("%d\n",-1); return;
}
maxflow=0;
for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
for (int i=1;i<=n+2;i++) {
// for (int j=head[i];j!=-1;j=edge[j].to) printf("%d %d %d\n",i,edge[j].u,edge[j].w);
}
dinic(T,S);
for (int i=head[S];i!=-1;i=edge[i].to) {
maxflow+=edge[i^1].w+edge[i].l;
}
printf("%d\n",maxflow);
}
int main() {
memset(head,-1,sizeof(head)); k=0;
scanf("%d",&n);
cnt=n+2;
S=n+1; T=n+2;
for (int i=1;i<=n;i++) {
add(S,i,0,INF); add(i,T,0,INF);
}
for (int i=1;i<=n;i++) {
scanf("%d",&x);
for (int j=1;j<=x;j++) {
scanf("%d",&y); add(i,y,1,INF);
}
}
add(T,S,0,INF);
upper_lower_maxflow(S,T);
}
考虑二分图,把行与列分别作为行与列的两个点集,如果(i,j)上没有障碍,那么就i->j连一条下界为0,上界为1的边
同时S->i连一条下界为Li,上界为INF的边,j->T连一条下界为Ci,上界为INF的边,跑一遍上下界网络流
i,j分别为行与列经过映射的点
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N=300;
const int M=50010;
const int INF=0x3f3f3f3f;
struct node{
int u,to,l,r,w;
};
node edge[M<<1];
int arrange[N],head[N],a[N],cur[N],ret_flow[N];
int b[110][110];
int n,cnt,S,T,k,x,y,m,k_sum;
void add(int x,int y,int l,int r) {
edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
}
void add_new(int x,int y,int z){
edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
}
bool bfs(int S,int T) {
queue<int>q;
q.push(S);
memset(arrange,0,sizeof(arrange));
arrange[S]=1;
while (!q.empty()) {
int v=q.front(); q.pop(); if (v==T) return 1;
for (int i=head[v];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (edge[i].w&&!arrange[u]) {
arrange[u]=arrange[v]+1;
q.push(u);
}
}
}
return 0;
}
int dfs(int now,int maxlow,int t) {
if (now==t) return maxlow;
int ret=0;
for (int &i=cur[now];i!=-1;i=edge[i].to) {
int u=edge[i].u;
if (arrange[u]==arrange[now]+1&&edge[i].w) {
int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
edge[i].w-=f; edge[i^1].w+=f;
if (ret==maxlow) return ret;
}
}
return ret;
}
int dinic(int S,int T) {
int ans=0;
while (bfs(S,T)) {
memcpy(cur,head,sizeof(head));
ans+=dfs(S,INF,T);
}
return ans;
}
void upper_lower_maxflow(int S,int T,int n) {
for (int i=1;i<=n+2;i++) {
for (int j=head[i];j!=-1;j=edge[j].to)
{
ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
}
}
int SS=n+3,TT=n+4;
int maxflow=0;
for (int i=1;i<=n+2;i++)
if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
if (maxflow!=dinic(SS,TT)) {
puts("JIONG!"); return;
}
maxflow=0;
for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
// for (int i=head[T];i!=-1;i=edge[i].to) printf("%d %d %d\n",T,edge[i].u,edge[i].w);
dinic(T,S);
for (int i=head[T];i!=-1;i=edge[i].to) {
maxflow+=edge[i].w+edge[i^1].l;
}
printf("%d\n",maxflow);
}
int main() {
memset(head,-1,sizeof(head)); k=0;
scanf("%d%d%d",&n,&m,&k_sum);
S=n+m+1; T=n+m+2;
for (int i=1;i<=n;i++) {
scanf("%d",&x); add(S,i,x,INF);
}
for (int i=1;i<=m;i++) {
scanf("%d",&x); add(n+i,T,x,INF);
}
for (int i=1;i<=k_sum;i++) {
scanf("%d%d",&x,&y); b[x][y]=1;
}
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++)
if (!b[i][j]) add(i,n+j,0,1);
}
add(T,S,0,INF);
upper_lower_maxflow(S,T,n+m);
}