SGU 176 上下界最小流

题意:在一个网络中有M条管道,有一些管道必须满流.这个网络中有标号为1 的源点和标号为N的汇点。求此网络流中的最小流。

做法:一般的上下界网络流,可以用来测试模板。

#include<stdio.h>
#include<string.h>
#define LMT 200
#define eps 2e9
typedef struct
{
    int u,v;
}line;
line e[LMT*LMT];
int mat[LMT][LMT],fmat[LMT][LMT],lim[LMT][LMT],lev[LMT],q[LMT];
int n,m,sum;
int bfs(int s,int t)
{
    int i,j,tail,head;
    memset(lev,0,sizeof(lev));
    lev[s]=1;head=tail=0;
    q[tail++]=s;
    while(head<tail)
    {
        i=q[head++];
        for(j=0;j<=n+1;j++)
        if(fmat[i][j]>0&&0==lev[j])
        {
            lev[j]=lev[i]+1;
            q[tail++]=j;
        }
    }
    return lev[t]!=0;
}
int dfs(int s,int t)
{
    int i,j,top=0,ret=0;
    q[top++]=s;
    while(top>0)
    {
        i=q[top-1];
        if(i==t)
        {
            int min,back;
            min=eps;
            for(i=1;i<top;i++)
            {
               if(min>fmat[q[i-1]][q[i]])
              {
                min=fmat[q[i-1]][q[i]];
                back=i;
              }
            }
            ret+=min;
            for(i=1;i<top;i++)
            {
                fmat[q[i-1]][q[i]]-=min;
                fmat[q[i]][q[i-1]]+=min;
            }
            top=back;
        }
        else
        {
            for(j=0;j<=n+1;j++)
            if(fmat[i][j]>0&&lev[j]==lev[i]+1)
            {
                q[top++]=j;
                break;
            }
            if(j>n+1)
            {
                lev[i]=0;
                top--;
            }
        }
    }
    return ret;
}
int dinic(int s,int t)
{
    int ret=0;
    while(bfs(s,t))
       ret+=dfs(s,t);
    return ret;
}
int test(int s,int t,int mid)
{
    int tn;
    sum=0;
    mat[n][1]=mid;
    memset(fmat,0,sizeof(fmat));
    int i,j;
    for(i=1;i<=n;i++)
    {
        tn=0;
      for(j=1;j<=n;j++)
      {
          tn+=lim[j][i]-lim[i][j];
        fmat[i][j]=mat[i][j]-lim[i][j];
      }
        if(tn>0)
        {
            fmat[s][i]=tn;
            sum+=tn;
        }
        else
             fmat[i][t]=-tn;
    }
    return dinic(s,t)>=sum;
}
int main()
{
    int l,r,i,j,c,d,mid,x,ans;
    scanf("%d%d",&n,&m);
        memset(mat,0,sizeof(mat));
        memset(lim,0,sizeof(lim));
        r=0;l=0;//这里的L必须为0,否则会出现错误,不知为何显示的PE
        for(x=0;x<m;x++)
        {
            scanf("%d%d%d%d",&i,&j,&c,&d);
            r+=c;
            mat[i][j]=c;
            if(d)
              lim[i][j]=c;
            e[x].u=i;
            e[x].v=j;
        }
        ans=-1;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(test(0,n+1,mid))
            {
                r=mid-1;
                ans=mid;
            }
            else l=mid+1;
        }
        if(ans==-1)
        {
           puts("Impossible");
           return 0;
        }
        else
        {
            test(0,n+1,ans);
            printf("%d\n",ans);
            for(i=0;i<m;i++)
               printf("%d%c",mat[e[i].u][e[i].v]-fmat[e[i].u][e[i].v],i==m-1?'\n':' ');
        }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值