网络流之上下界网络流zoj zju 3229
先判断有无可行流,再求最大流
http://blog.csdn.net/fp_hzq/article/details/6772320
在上一题上下界流中,我们已经知道了怎么判断无源汇上下界网络流有无可行流
把每一天看成一个点,每个女孩也看成一个点,增加源和汇s、t,源向每一天连上[0,d]的边,每一天与每个女孩如果有拍照任务的话连上[l,r]的边,每个女孩与汇连上[g,oo]的边,于是构成一个有上下界的图
所以这道题目我们可以转换一下
只要连一条T → S的边,流量为无穷,没有下界,那么原图就得到一个无源汇的循环流图。接下来的事情一样:原图中的边的流量设成自由流量ci – bi。新建源点SS汇点TT,求Mi,连边。然后求SS → TT最大流,判是否满流。
判定有解之后然后求最大流,信息都在上面求得的残留网络里面。满足所有下界时,从s → t的流量为后悔边s → t的边权!然后在残留网络中s → t可能还有些自由流没有流满,再做一次s → t的最大流,所得到的最大流就是原问题的最大流(内含两部分:残留的自由流所得到的流+后悔边s → t)。
一些细节在代码中有注释
View Code
#include<stdio.h>
#include<string.h>
const int MAX=2000;
const int INF=1000000000;
struct
{
int v,c,next;
}edge[1000000];
int E,head[MAX];
int gap[MAX],cur[MAX];
int pre[MAX],dis[MAX];
void add_edge(int s,int t,int c,int cc)
{
edge[E].v=t; edge[E].c=c;
edge[E].next=head[s];
head[s]=E++;
edge[E].v=s; edge[E].c=cc;
edge[E].next=head[t];
head[t]=E++;
}
int min(int a,int b){return (a==-1||b<a)?b:a;}
int SAP(int s,int t,int n)
{
memset(gap,0,sizeof(gap));
memset(dis,0,sizeof(dis));
int i;
for(i=0;i<n;i++)cur[i]=head[i];
int u=pre[s]=s,maxflow=0,aug=-1,v;
gap[0]=n;
while(dis[s]<n)
{
loop: for(i=cur[u];i!=-1;i=edge[i].next)//邻接表结构会把邻接点都搜到,所以如果没有删掉某些边,图中有几个点就传进几个点。
{
v=edge[i].v;
if(edge[i].c>0&&dis[u]==dis[v]+1)
{
aug=min(aug,edge[i].c);
pre[v]=u;
cur[u]=i;
u=v;
if(u==t)
{
for(u=pre[u];v!=s;v=u,u=pre[u])
{
edge[cur[u]].c-=aug;
edge[cur[u]^1].c+=aug;
}
maxflow+=aug;
aug=-1;
}
goto loop;
}
}
int mindis=n;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].c>0&&dis[v]<mindis)
{
cur[u]=i;
mindis=dis[v];
}
}
if((--gap[dis[u]])==0)break;
gap[dis[u]=mindis+1]++;
u=pre[u];
}
return maxflow;
}
int n,m,e;
int s,t,ss,tt;
int in[MAX],low[100010];
bool solve()
{
int i,j,k;ss=n+m+2;tt=ss+1;
for(i=0;i<=n+m+1;i++)
{
if(in[i]>0) add_edge(ss,i,in[i],0);
if(in[i]<0) add_edge(i,tt,-in[i],0);
}
SAP(ss,tt,tt+1);
for(i=head[ss];i!=-1;i=edge[i].next)
if(edge[i].c) return false;
return true;
}
int d[MAX];
int main()
{
int i,j,k,up,T,g,c;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));
s=0;t=n+m+1;E=0;
memset(in,0,sizeof(in));
for(i=1;i<=m;i++)
{
scanf("%d",&g);
in[t]+=g;
in[i+n]-=g;
}
e=0;
for(i=1;i<=n;i++)
{
scanf("%d%d",&c,&d[i]);
for(j=1;j<=c;j++)
{
scanf("%d%d%d",&T,&low[++e],&up);
add_edge(i,T+n+1,up-low[e],0);
in[i]-=low[e];
in[T+n+1]+=low[e];
}
}
for(i=1;i<=n;i++) add_edge(s,i,d[i],0);
for(i=1;i<=m;i++) add_edge(i+n,t,INF,0);
add_edge(t,s,INF,0);//s->t : 0 ,t->s : INF 构造无源汇上下界网络
int ans=0;
if(solve())
{
//虽然求s、t间的最大流,但是总的点数还是要传 tt+1 (n+m+4),即要把ss、tt算进去,因为在最大流模板中会搜到tt
int max_flow=SAP(s,t,tt+1);add_edge(t,s,-INF,0);//把s-t之间的边去掉,防止对最大流计算的影响
for(i=head[s];i!=-1;i=edge[i].next) ans+=edge[i^1].c;//把从s出发的边的反边的流量都加起来就是最大流,如果没有去掉s-t之间的边,会把INF加进来
printf("%d\n",ans);
for(i=1;i<=e;i++) printf("%d\n",edge[(i<<1)-1].c+low[i]);
}
else printf("-1\n");
puts("");
}
return 0;
}