POJ 2396 (有上下界的最大流)

题意:现在有一个n*m的方阵,方阵里面的数字未知,但是我们知道如下约束条件:
            1> 每一行的数字的和 
            2> 每一列的数字的和 
            3> 某些格子有特殊的大小约束,用大于号,小于号和等于号表示 
   求解是否存在在满足所有的约束的条件下用正数来填充该方阵的方案,若有,输出填充后的方阵,否则输出IMPOSSIBLE.
   这道题可以转化成容量有上下界的最大流问题,将方阵的行从1……n编号,列n+1……n+m编号,添加源点s=0和汇点t=n+m+1.
            1> 如果u行v列的数字大于w,则将行节点u和列节点v+n相连,w为该边容量的下界,即置low[u][v+n]=max(low[u][v+n],w+1)
            2> 如果u行v列的数字小于w,则将行节点u和列节点v+n相连,w为该边容量的上界,即置high[u][v+n]=min(high[u][v+n],w-1)
            3> 如果u行v列的数字等于w,则将行节点u和列节点v+n相连,w既为该边容量的下界同时也为该边容量的上界,

           即置low[u][v+n]=high[u][v+n]=w

求解有上下界的最大流方法 :
      已知一个原网络,每条边的容量有上下界 。
      1.  对于每条边(u,v),将容量改为它的上下界之差
      2.  增加一个附加源点S,一个附加汇点T,
      3.  每个节点与 S连边 ,容量为所有进入该节点的边的下界之和
      4.  给个节点与T连边, 容量为所有从该节点出去的边的下界之和
      5.给原网络的源点汇点连边, add(t , s , inf) ;
 

先建原网络 ,  
    s到每个行节点,容量为0 ,(上下界都是行之和的值,所以相减为0)
    每个对边(u,v) ,连边,容量为上下界的容量差 ; 
   每个列节点与汇点连边,容量为0 ;(上下界都是行之和的值,所以相减为0)

再从原网络构造伴随网络求最大流 :
     增加一个附加源点S,和附加汇点T ,
     每个节点与S连边,容量为,进入u点的所有边的下界之和;
     
于是为了满足流量守恒,D(U)是从u出发的弧的下界之和-进入u的弧的下界之和。
(1)如果 D(u) < 0,那么有 S-> u 添加流量D(u)的边
(2)如果 D(u) > 0,那么有 u -> T 添加流量 - D(u)的边
    再给原网络的源点汇点连边, (t , s , inf ) ;
从附加源点到附加汇点求一遍最大流 ,判断从S出去的边,流量是否均满流, 如果不满则无解,否则 , 原网络的最大流可行流就是  就是该伴随网络上的流量加上下界容量 ;
 
#include<stdio.h>
#include<string.h>
#define maxn 504
#define maxm 100003
#define inf 1000000000

int min(int a, int b)
{
    return a < b ? a : b;
}
int max(int a, int b)
{
    return a > b ? a : b;
}

struct E
{
    int v, next, c;
}edge[maxm];

int head[maxn], tot;
int n, m;
int S, T;
int s, t;

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}

void add(int s, int t, int c)
{
    edge[tot].v = t;
    edge[tot].c = c;
    edge[tot].next = head[s];
    head[s] = tot++;
    edge[tot].v = s;
    edge[tot].c = 0;
    edge[tot].next = head[t];
    head[t] = tot++;
}

int gap[maxn], dis[maxn], cur[maxn], pre[maxn];
int sap(int s, int t, int vs)
{
    int i;
    for(i = 0; i <= vs; i++)
    {
        dis[i] = gap[i] = 0;
        cur[i] = head[i];
    }
    gap[0] = vs;
    int u = pre[s] = s, maxf = 0, aug = inf, v;
    while(dis[s] < vs)
    {
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)
                 {
                     while(u != s)
                     {
                         u = pre[u];
                         edge[cur[u]].c -= aug;
                         edge[cur[u]^1].c += aug;
                     }
                     maxf += aug;
                     aug = inf;
                 }
                 goto loop;
             }
         }
         int min_d = vs;
         for(i = head[u]; i != -1; i = edge[i].next)
         {
             v = edge[i].v;
             if(edge[i].c > 0 && dis[v] < min_d)
             {
                 min_d = dis[v];
                 cur[u] = i;
             }
         }
         if(!(--gap[dis[u]])) break;
         ++gap[dis[u] = min_d + 1];
         u = pre[u];
    }
    return maxf;
}

int low[maxn][maxn], high[maxn][maxn];
int in[maxn];
bool judge()
{
    int i, j;
    for(i = head[S]; i != -1; i = edge[i].next)
    {
        if(edge[i].c && i % 2 == 0)
            return 0;
    }
    return 1;
}

int ans[maxn][maxn];
void solve()
{
    int i, j;
    for(i = 1; i <= n; i++)
        for(j = head[i]; j != -1; j = edge[j].next)
        {
            int v = edge[j].v;
            ans[i][v-n] = edge[j^1].c += low[i][v-n];
        }
    for(i = 1; i <= n; i++)
    {
        for(j = 1; j < m; j++)
            printf("%d ", ans[i][j]);
        printf("%d\n", ans[i][j]);
    }
}


int main()
{
    int i, j, cas;
    int x, y, z;
    char buf[11];
    scanf("%d", &cas);
    while(cas--)
    {
        scanf("%d%d", &n, &m);
        for(i = 0; i <= n; i++)
            for(j = 0; j <= m; j++)
                low[i][j] = 0, high[i][j] = inf; //下界,上届容量 
        init();
        s = 0; t = n+m+1; S = t+1; T = S+1;
        memset(in, 0, sizeof(in));
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &z);
            in[i] += z;
            in[s] -= z;
            add(s, i, 0);  //源点与行节点连边, 
        }
        for(i = 1; i <= m; i++)
        {
            scanf("%d", &z);
            in[i+n] -= z;
            in[t] += z;
            add(i+n, t, 0); //汇点与节点连边 
        }
        int w;
        scanf("%d", &w);
        bool flag = 1;
        while(w--)
        {
            scanf("%d%d%s%d", &x, &y, buf, &z);
            if(!flag) continue;
            int a, b, aa, bb;
            a = aa = x;
            b = bb = y;
            if(!x) a = 1, aa = n; 
            if(!y) b = 1, bb = m;
            if(buf[0] == '=')
                for(i = a; i <= aa; i++)
                    for(j = b; j <= bb; j++)
                    {
                        if(low[i][j] <= z && high[i][j] >= z)
                            low[i][j] = high[i][j] = z;
                        else flag = 0;
                    }
            if(buf[0] == '>')
            {
                if(z < 0) continue;
                for(i = a; i <= aa; i++)
                    for(j = b; j <= bb; j++)
                    {
                        low[i][j] = max(low[i][j], z+1);
                        if(low[i][j] > high[i][j])
                            flag = 0;
                    }
            }
            if(buf[0] == '<')
            {
                if(z <= 0) flag = 0;
                for(i = a; i <= aa; i++)
                    for(j = b; j <= bb; j++)
                    {
                        high[i][j] = min(high[i][j], z-1);
                        if(low[i][j] > high[i][j])
                            flag  = 0;
                    }
            }
        }
        if(!flag) { printf("IMPOSSIBLE\n"); continue;}
        add(t, s, inf);
        for(i = 1; i <= n; i++)
            for(j = 1; j <= m; j++)
            {
                in[j+n] += low[i][j];
                in[i] -= low[i][j];
                add(i, j+n, high[i][j] - low[i][j]);//修改容量 
            }
        for(i = 0; i <= n+m+1; i++)
        {               
            if(in[i] < 0) add(i, T, -in[i]);
            if(in[i] > 0) add(S, i, in[i]);
        }     
        sap(S, T, T+1);
        if(!judge())
            printf("IMPOSSIBLE\n");
        else solve();
        if(cas) puts("");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值