SGU 176 Flow construction(有源有汇上下界最小流)

题意:求从1到N的最小流,有些边必须满流。


思路:满流的看做下界和上界相同。。。首先要判断一下是否有可行流,这个比较简单,然后是最小流,图论书上的做法一般是从N到1做一遍最大流,通过这个操作来得到最小流,但是对于这个题并不完全适用,这道题有些管道可以形成环,并且一开始就有流量,因此,使用上面的方法可能会破坏环从而使流量变大,如果有这种情况发生,就说明这个网络的最小流是可以为0的,因此,如果遇到这种情况,那么直接令从N->1的弧的容量为0,然后求解可行流,这个可行流就是最小流。还有二分的方法,不断二分从源点流出的流量,判断是否有可行流就行了。这题PE了半天发现是数组开小了……SGU动不动就给PE真是让人伤心……这两个方法都写到一份代码里了,速度都差不多……


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100+10;
const int maxm=100000+10;
struct Edge
{
    int v,next,cap,flow;
    Edge (){};
    Edge(int vv,int nx,int cc,int ff) {v=vv;next=nx;cap=cc;flow=ff;}
}edges[maxm<<1];
int head[maxn],bcnt[maxm],nEdge;
int cur[maxn],d[maxn],ins[maxn],outs[maxn],N;
bool vis[maxn];
void AddEdge(int u,int v,int cap)
{
    edges[++nEdge]=Edge(v,head[u],cap,0);
    head[u]=nEdge;
    edges[++nEdge]=Edge(u,head[v],0,0);
    head[v]=nEdge;
}
void Init()
{
    memset(head,0xff,sizeof(head));
    memset(bcnt,0,sizeof(bcnt));
    memset(ins,0,sizeof(ins));
    memset(outs,0,sizeof(outs));
    nEdge=-1;
}
bool BFS(int S,int T)
{
    memset(vis,0,sizeof(vis));
    memset(d,0x3f,sizeof(d));
    vis[S]=true;d[S]=0;
    queue<int>q;
    q.push(S);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int k=head[u];k!=-1;k=edges[k].next)
        {
            Edge e =edges[k];
            if(!vis[e.v]&&e.cap>e.flow)
            {
                d[e.v]=d[u]+1;
                vis[e.v]=true;
                q.push(e.v);
            }
        }
    }
    return vis[T];
}
int DFS(int u,int T,int a)
{
    if(u==T||a==0) return a;
    int flow=0,f;
    for(int &k=cur[u];k!=-1;k=edges[k].next)
    {
        Edge e=edges[k];
        if(d[e.v]==d[u]+1&&(f=DFS(e.v,T,min(a,e.cap-e.flow)))>0)
        {
            edges[k].flow+=f;
            edges[k^1].flow-=f;
            flow+=f;a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int Maxflow(int S,int T)
{
    int flow=0;
    while(BFS(S,T))
    {
        for(int i=0;i<=N;++i) cur[i]=head[i];
        flow+=DFS(S,T,inf);
    }
    return flow;
}
int f(int l,int r,int sum)
{
    int res=-1,m;
    while(l<=r)
    {
        m=(l+r)>>1;
        edges[nEdge^1].cap=m;
        if(Maxflow(0,N)==sum)
        {
            res=m;
            r=m-1;
        }
        else l=m+1;
        for(int i=0;i<=nEdge;++i)
            edges[i].flow=0;
    }
    if(res!=-1)
    {
        for(int i=0;i<=nEdge;++i)
            edges[i].flow=0;
        edges[nEdge^1].cap=res;
        Maxflow(0,N);
    }
    return res;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    Init();
    int n,m,sum=0;
    int u,v,z,c;
    scanf("%d%d",&n,&m);
    N=n+1;
    for(int i=0;i<m;++i)
    {
        scanf("%d%d%d%d",&u,&v,&z,&c);
        bcnt[i]=z*c;
        if(c) {ins[v]+=z,outs[u]+=z;}
        AddEdge(u,v,z-z*c);
    }
    for(int i=1;i<=n;++i)
    {
        sum+=ins[i];
        if(ins[i]) AddEdge(0,i,ins[i]);
        if(outs[i]) AddEdge(i,N,outs[i]);
    }
    AddEdge(n,1,inf);
     //使用下面注释掉的二分得到最终结果
    //int ans=f(0,10000000,sum);
    if(Maxflow(0,N)!=sum)
    {
        printf("Impossible\n");
        return 0;
    }
    int ans=edges[nEdge-1].flow;
    edges[nEdge].cap=edges[nEdge^1].cap=0;
    edges[nEdge].flow=edges[nEdge^1].flow=0;
    int tmp=Maxflow(n,1);
    ans-=tmp;
    if(ans<0)
    {
        for(int i=0;i<=nEdge;++i)
            edges[i].flow=0;
        Maxflow(0,N);
        ans=0;
    }
    printf("%d\n",ans);
    for(int i=0;i<m;++i)
    {
        if(i) printf(" ");
        printf("%d",edges[i*2].flow+bcnt[i]);
    }
    printf("\n");
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值