[POJ2396]Budget(有源汇有上下界的可行流+讲解)

题目:

我是超链接

题意:

有一个n*m的非负整数矩阵(n<=20,m<=200),给定每行之和和每列之和,并对一些格子进行约束
例如:
1 2 > 2代表(1,2)格子里的数大于2
0 1 = 2表示第1列的所有数字都等于2
求一种可能的矩阵

题解:

这怎么既有行又有列?我要是设置了行和列怎么代表点呢?诶等等,一行+一列不就对应一个点吗= =
那么我们建图就设置两列,一列表示 行,一列表示 列,连边代表限制,行与列之间的连边代表点的限制,但他们既然是一个矩阵,和上一道题那个一圈的管道不一样,本身就是有源有汇的
xi表示行,yi表示列,s-xi[sumx,sumx],yi-t[sumy,sumy]
和无源汇有上下界的可行流类似,只需要加一条边t->s,限制为[0,inf]就可以了,相当于是让源点和汇点也满足流量平衡
然后再建立附加源汇ss,tt,其余的就和无源汇的一样了
证明什么的都类似无源汇啦

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define INF 1e9
const int N=15000;
int tot=-1,maxflow,in,out,point[N],nxt[N],v[N],remind[N],cur[N],dis[N],d[N];
int l[205][500],r[205][500],pipe[205][500];
void cl()
{
    in=out=0;maxflow=0;memset(l,0,sizeof(l));memset(r,0x7f,sizeof(r));
    tot=-1;memset(point,-1,sizeof(point));memset(nxt,-1,sizeof(nxt));
    memset(d,0,sizeof(d));
}
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));
    for (int i=1;i<=t;i++) cur[i]=point[i];
    dis[s]=0;
    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()
{
    int T,n,m,x,lj,y,z,s,t,ss,tt;char fh[5];
    scanf("%d",&T);
    while (T--)
    {
        cl();
        scanf("%d%d",&n,&m); 
        s=n+m+1; t=s+1; ss=t+1; tt=ss+1;
        for (int i=1;i<=n;i++) scanf("%d",&x),d[s]-=x,d[i]+=x;
        for (int i=1;i<=m;i++) scanf("%d",&x),d[i+n]-=x,d[t]+=x;
        scanf("%d",&lj);
        while (lj--)
        {
            scanf("%d%d%s%d",&x,&y,&fh,&z);
            if (!x && !y)
            {
                for (int i=1;i<=n;i++)
                  for (int j=1;j<=m;j++)
                  {
                    if (fh[0]=='=') l[i][j]=r[i][j]=z;
                    if (fh[0]=='>') l[i][j]=max(l[i][j],z+1);
                    if (fh[0]=='<') r[i][j]=min(r[i][j],z-1);   
                  }
            }
            else if (!y)
            {
                for (int j=1;j<=m;j++)
                {
                    if (fh[0]=='=') l[x][j]=r[x][j]=z;
                    if (fh[0]=='>') l[x][j]=max(l[x][j],z+1);
                    if (fh[0]=='<') r[x][j]=min(r[x][j],z-1);   
                }
            }
            else if (!x)
            {
                for (int i=1;i<=n;i++)
                {
                    if (fh[0]=='=') l[i][y]=r[i][y]=z;
                    if (fh[0]=='>') l[i][y]=max(l[i][y],z+1);
                    if (fh[0]=='<') r[i][y]=min(r[i][y],z-1);   
                }
            }
            else 
            {
                if (fh[0]=='=') l[x][y]=r[x][y]=z;
                if (fh[0]=='>') l[x][y]=max(l[x][y],z+1);
                if (fh[0]=='<') r[x][y]=min(r[x][y],z-1);   
            }
        }
        addline(t,s,INF);
        for (int i=1;i<=n;i++)
          for (int j=1;j<=m;j++)
            addline(i,j+n,r[i][j]-l[i][j]),d[i]-=l[i][j],d[j+n]+=l[i][j],pipe[i][j]=tot;
        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];
        }
        if (in!=out) {printf("IMPOSSIBLE\n\n");continue;}
        while (bfs(ss,tt)) maxflow+=dfs(ss,tt,INF);
        if (maxflow!=in) {printf("IMPOSSIBLE\n\n");continue;}
        else 
        {
            for (int i=1;i<=n;i++)
            {
              for (int j=1;j<m;j++) printf("%d ",remind[pipe[i][j]]+l[i][j]);
              printf("%d\n",remind[pipe[i][m]]+l[i][m]);
            }
            printf("\n");
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值