[BZOJ3698]XWW的难题(有源汇上下界最大流+讲解)

题目:

我是超链接

题解:

建图的话和有源汇可行流一样

求解方法:
在新图上跑ss到tt的最大流(附加源汇)
若新图满流,那么一定存在一种可行流
记此时 f(s,i)=sum1
将t->s这条边拆掉,在新图上跑s到t的最大流(原源汇)
记此时 f(s,i)=sum2
最终答案即为 sum1+sum2

证明大概是这样的:
添加附加源汇的作用:为了满足流量平衡条件,在新图中进行相应的补流或分流
只要连接附加源汇的边满流,新图中s->t的任意一种可行流都是原图的可行流
跑ss->tt的最大流了之后,相当于是使连接附加源汇的边满流,进而求出了一种可行流
再将t->s的边拆掉(使s->t变成一个有源汇的网络流图),跑s到t的最大流,加上跑出来的最大流即为原图中一种可行的最大流(并不是重新连边,而是只拆掉t->s的边,也就是再增广一次!)

那么对于这道题目来说,有了上一道题的启发,我们知道这种【矩阵,和,限制,最大值】可以转化成网络流问题,源点连行,边为限制的和;列连汇点,边为限制的和;行与列之间相连,限制为相应点的限制;他们都和附加源汇相连,补充流量;连t->s的INF边(最大流应求一遍之后去掉)

这道题原图的建图方法是:
对于每一行i,s->i,[a(i,n),a(i,n)+1]
对于每一列j,j->t,[a(n,j),a(n,j)+1]
对于每一个点(i,j),i->j,[a(i,j),a(i,j)+1]
然后再按照有源汇有上下界对这个图进行改造即可

代码:

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define INF 1e9
using namespace std;
const int N=100005;
int tot=-1,nxt[N],point[N],v[N],cur[N],remind[N],dis[N],r[105][105],l[105][105],pip[105][105],d[N];double ll[105] [105];
void addline(int x,int y,int cap)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=cap;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0;
}
int dfs(int now,int t,int limit)
{
    if (now==t || !limit) return limit;
    int flow=0,f;
    for (int i=cur[now];i!=-1;i=nxt[i])
    {
        cur[now]=i;
        if (dis[v[i]]==dis[now]+1 && (f=dfs(v[i],t,min(limit,remind[i]))))
        {
            limit-=f;
            flow+=f;
            remind[i]-=f;
            remind[i^1]+=f;
            if (!limit) break;
        }
    }
    return flow;
}
bool bfs(int s,int t)
{
    queue<int>q;
    q.push(s);
    memset(dis,0x7f,sizeof(dis));
    dis[s]=0;
    for (int i=1;i<=t;i++) cur[i]=point[i];
    while (!q.empty())
    {
        int x=q.front(); q.pop();
        for (int i=point[x];i!=-1;i=nxt[i])
          if (dis[v[i]]>INF && remind[i])
          {q.push(v[i]); dis[v[i]]=dis[x]+1;}
    }
    return dis[t]<INF;
}
int main()
{
    memset(point,-1,sizeof(point));memset(nxt,-1,sizeof(nxt));
    int n,s,t,ss,tt;
    scanf("%d",&n);
    s=n*2+1; t=s+1; ss=t+1; tt=ss+1;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++) 
        scanf("%lf",&ll[i][j]),r[i][j]=ceil(ll[i][j]),l[i][j]=floor(ll[i][j]);

    for (int i=1;i<n;i++)
    {
        d[s]-=l[i][n],d[i]+=l[i][n],addline(s,i,r[i][n]-l[i][n]);
        d[i+n]-=l[n][i],d[t]+=l[n][i],addline(i+n,t,r[n][i]-l[n][i]);
    }
    for (int i=1;i<n;i++)
      for (int j=1;j<n;j++)
        d[i]-=l[i][j],d[j+n]+=l[i][j],addline(i,j+n,r[i][j]-l[i][j]),pip[i][j]=tot;
    int in=0,out=0;
    for (int i=1;i<=t;i++)
    {
        if (d[i]>0) addline(ss,i,d[i]),in+=d[i];
        if (d[i]<0) addline(i,tt,-d[i]),out-=d[i];
    }
    addline(t,s,INF);int lj=tot;
    if (in!=out) {printf("No");return 0;}
    int maxflow=0,ans=0;

    while (bfs(ss,tt)) maxflow+=dfs(ss,tt,INF);

    if (maxflow!=in) {printf("No");return 0;}

    remind[lj]=remind[lj^1]=0;

    while (bfs(s,t)) maxflow+=dfs(s,t,INF);

    for (int i=1;i<n;i++)
      for (int j=1;j<n;j++)
        ans+=remind[pip[i][j]]+l[i][j];

    printf("%d",ans*3);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值