ZOJ1994有源汇上下界可行流

http://fastvj.rainng.com/contest/236779#problem/G

Description:

  n 行 m 列

  给你行和 与 列和

  然后有Q个限制,表示特定单元格元素大小的范围,最后问你可行的矩阵值

Solution:
  有源汇上下界最大流问题,初始源点 连 行和流量是该行对应得行和,然后列连初始汇点,容量为列的列和,详细的限制,对应于行列之间,因为我最后要输出一个矩阵,所以n * m每一条边都要链接最后,手动连一条 t s inf的边,变成无源汇有上下界可行流问题,然后拆边,把对应边的流量,放到数组里,输出就好啦

Code:

  前面比较基础的Dinic,数据操作函数,加边操作

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf (1 << 28)
using namespace std;
const int maxn = 25;
const int maxm = 210;
const int mn = 505;
const int mm = 440020;
struct node{
    int to,val,pre,lid;
}e[mm];
int id[mn],cnt=0;

int cur[mn];
int flor[mn];

int upflow[mn];
int lowf[maxm][maxn];
int upf[maxm][maxn];
int out[maxm][maxn];
void init()
{
    memset(id,-1,sizeof(id));
    memset(upflow,0,sizeof(upflow));
    cnt = 0;
}
void add(int from,int to,int val,int lid)
{
    e[cnt].to = to;
    e[cnt].val = val;
    e[cnt].lid = lid;
    e[cnt].pre = id[from];
    id[from] = cnt++;
    swap(from,to);
    e[cnt].to = to;
    e[cnt].val = 0;
    e[cnt].lid = lid;
    e[cnt].pre = id[from];
    id[from] = cnt++;
}
void addflow(int from,int to,int low,int up,int lid)
{
    upflow[from] -= low;
    upflow[to] += low;
    add(from,to,up-low,lid);
}
bool bfs(int s,int t)
{
    memset(flor,0,sizeof(flor));
    flor[s] = 1;
    queue<int> q;
    while(q.size())q.pop();

    q.push(s);
    while(q.size())
    {
        int now = q.front();
        q.pop();

        for(int i = id[now];~i;i = e[i].pre)
        {
            int to = e[i].to;
            int val = e[i].val;
            if(val > 0 && flor[to] == 0)
            {
                flor[to] = flor[now] + 1;
                q.push(to);
                if(to == t)
                    return true;
            }
        }
    }
    return false;
}
int dfs(int s,int t,int value)
{
    if(s == t || value == 0)return value;

    int ret = value,a;

    for(int &i = cur[s];~i;i = e[i].pre)
    {
        int val = e[i].val;
        int to = e[i].to;
        if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
        {
            e[i].val -= a;
            e[i^1].val += a;
            ret -= a;
            if(ret == 0)break;
        }
    }
    if(ret == value)flor[s] = 0;
    return value - ret;
}

int Dinic(int s,int t)
{
    int ret = 0;
    while(bfs(s,t))
    {
        memcpy(cur,id,sizeof(id));
        ret += dfs(s,t,inf);
    }
    return ret;
}

void addlimit(int i,int j,char op,int lim)
{
    if(op == ‘=‘)
    {
        upf[i][j] = lowf[i][j] = lim;
    }
    else if(op == ‘>‘)
    {
        lowf[i][j] = max(lowf[i][j],lim+1);
    }
    else
    {
        upf[i][j] = min(upf[i][j],lim-1);
    }
}

然后根据输入一点点的加边,这是行和和列和对应的边

scanf("%d%d",&n,&m);
        s = 0;
        t = n + m + 1;
        ss = t + 1;
        tt = ss + 1;
        int lsum;
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&lsum);
            addflow(s,i,lsum,lsum,0);
        }
        for(int i = 1;i <= m;++i)
        {
            scanf("%d",&lsum);
            addflow(n + i,t,lsum,lsum,0);
        }
        int limitnum;

然后根据题目中给出的限制条件,填充上下界数组

scanf("%d",&limitnum);
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= m;++j)
            {
                lowf[i][j] = 0;
                upf[i][j] = inf;
            }
        }
        int row,col,lim;
        char op;
        for(int i = 1;i <= limitnum;++i)
        {
            scanf("%d %d %c %d",&row,&col,&op,&lim);
            if(row == 0 && col == 0)
            {
                for(int i = 1;i <= n;++i)
                {
                    for(int j = 1;j <= m;++j)
                    {
                        addlimit(i,j,op,lim);
                    }
                }
            }
            else if(row == 0)
            {
                for(int i = 1;i <= n;++i)
                {
                    addlimit(i,col,op,lim);
                }
            }
            else if(col == 0)
            {
                for(int i = 1;i <= m;++i)
                {
                    addlimit(row,i,op,lim);
                }
            }
            else
            {
                addlimit(row,col,op,lim);

            }
        }

根据上下界数组,添加有上下界边

int tot = 0;
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= m;++j)
            {
                addflow(i,n+j,lowf[i][j],upf[i][j],++tot);
            }
        }

加边完成后,转化为无源汇有上下界可行流 ———— 循环流

add(t,s,inf,0);
        int sum = 0;
        for(int i = s;i <= t;++i)
        {
            if(upflow[i] < 0)
            {
                add(i,tt,-upflow[i],0);
            }
            else
            {
                sum += upflow[i];
                add(ss,i,upflow[i],0);
            }
        }

跑DIinc,如果存在可行流的话

就输出叭~~

for(int now = n+1;now <= n + m;++now)
            {
                for(int i = id[now];~i;i = e[i].pre)
                {
                    int to = e[i].to;
                    int lid = e[i].lid;
                    if(lid == 0 || i % 2 == 0)continue;
                    out[to][now-n] = lowf[to][now-n] + e[i].val;
                }
            }
            for(int i = 1;i <= n;++i)
            {
                for(int j = 1;j <= m;++j)
                {
                    if(j == 1)
                        printf("%d",out[i][j]);
                    else
                        printf(" %d",out[i][j]);
                }
                printf("\n");
            }

完整代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf (1 << 28)
using namespace std;
const int maxn = 25;
const int maxm = 210;
const int mn = 505;
const int mm = 440020;
struct node{
    int to,val,pre,lid;
}e[mm];
int id[mn],cnt=0;

int cur[mn];
int flor[mn];

int upflow[mn];
int lowf[maxm][maxn];
int upf[maxm][maxn];
int out[maxm][maxn];
void init()
{
    memset(id,-1,sizeof(id));
    memset(upflow,0,sizeof(upflow));
    cnt = 0;
}
void add(int from,int to,int val,int lid)
{
    e[cnt].to = to;
    e[cnt].val = val;
    e[cnt].lid = lid;
    e[cnt].pre = id[from];
    id[from] = cnt++;
    swap(from,to);
    e[cnt].to = to;
    e[cnt].val = 0;
    e[cnt].lid = lid;
    e[cnt].pre = id[from];
    id[from] = cnt++;
}
void addflow(int from,int to,int low,int up,int lid)
{
    upflow[from] -= low;
    upflow[to] += low;
    add(from,to,up-low,lid);
}
bool bfs(int s,int t)
{
    memset(flor,0,sizeof(flor));
    flor[s] = 1;
    queue<int> q;
    while(q.size())q.pop();

    q.push(s);
    while(q.size())
    {
        int now = q.front();
        q.pop();

        for(int i = id[now];~i;i = e[i].pre)
        {
            int to = e[i].to;
            int val = e[i].val;
            if(val > 0 && flor[to] == 0)
            {
                flor[to] = flor[now] + 1;
                q.push(to);
                if(to == t)
                    return true;
            }
        }
    }
    return false;
}
int dfs(int s,int t,int value)
{
    if(s == t || value == 0)return value;

    int ret = value,a;

    for(int &i = cur[s];~i;i = e[i].pre)
    {
        int val = e[i].val;
        int to = e[i].to;
        if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
        {
            e[i].val -= a;
            e[i^1].val += a;
            ret -= a;
            if(ret == 0)break;
        }
    }
    if(ret == value)flor[s] = 0;
    return value - ret;
}

int Dinic(int s,int t)
{
    int ret = 0;
    while(bfs(s,t))
    {
        memcpy(cur,id,sizeof(id));
        ret += dfs(s,t,inf);
    }
    return ret;
}

void addlimit(int i,int j,char op,int lim)
{
    if(op == ‘=‘)
    {
        upf[i][j] = lowf[i][j] = lim;
    }
    else if(op == ‘>‘)
    {
        lowf[i][j] = max(lowf[i][j],lim+1);
    }
    else
    {
        upf[i][j] = min(upf[i][j],lim-1);
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    int n,m,s,t,ss,tt;
    while(T--)
    {
        init();
        scanf("%d%d",&n,&m);
        s = 0;
        t = n + m + 1;
        ss = t + 1;
        tt = ss + 1;
        int lsum;
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&lsum);
            addflow(s,i,lsum,lsum,0);
        }
        for(int i = 1;i <= m;++i)
        {
            scanf("%d",&lsum);
            addflow(n + i,t,lsum,lsum,0);
        }
        int limitnum;
        scanf("%d",&limitnum);
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= m;++j)
            {
                lowf[i][j] = 0;
                upf[i][j] = inf;
            }
        }
        int row,col,lim;
        char op;
        for(int i = 1;i <= limitnum;++i)
        {
            scanf("%d %d %c %d",&row,&col,&op,&lim);
            if(row == 0 && col == 0)
            {
                for(int i = 1;i <= n;++i)
                {
                    for(int j = 1;j <= m;++j)
                    {
                        addlimit(i,j,op,lim);
                    }
                }
            }
            else if(row == 0)
            {
                for(int i = 1;i <= n;++i)
                {
                    addlimit(i,col,op,lim);
                }
            }
            else if(col == 0)
            {
                for(int i = 1;i <= m;++i)
                {
                    addlimit(row,i,op,lim);
                }
            }
            else
            {
                addlimit(row,col,op,lim);

            }
        }
        int tot = 0;
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= m;++j)
            {
                addflow(i,n+j,lowf[i][j],upf[i][j],++tot);
            }
        }
        add(t,s,inf,0);
        int sum = 0;
        for(int i = s;i <= t;++i)
        {
            if(upflow[i] < 0)
            {
                add(i,tt,-upflow[i],0);
            }
            else
            {
                sum += upflow[i];
                add(ss,i,upflow[i],0);
            }
        }
        if(Dinic(ss,tt) == sum)
        {
            for(int now = n+1;now <= n + m;++now)
            {
                for(int i = id[now];~i;i = e[i].pre)
                {
                    int to = e[i].to;
                    int lid = e[i].lid;
                    if(lid == 0 || i % 2 == 0)continue;
                    out[to][now-n] = lowf[to][now-n] + e[i].val;
                }
            }
            for(int i = 1;i <= n;++i)
            {
                for(int j = 1;j <= m;++j)
                {
                    if(j == 1)
                        printf("%d",out[i][j]);
                    else
                        printf(" %d",out[i][j]);
                }
                printf("\n");
            }
        }
        else
        {
            printf("IMPOSSIBLE\n");
        }
        if(T)printf("\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/DF-yimeng/p/9740863.html

时间: 2024-10-08 15:35:17

ZOJ1994有源汇上下界可行流的相关文章

有源汇上下界可行流(POJ2396)

题意:给出一个n*m的矩阵的每行和及每列和,还有一些格子的限制,求一组合法方案. 源点向行,汇点向列,连一条上下界均为和的边. 对于某格的限制,从它所在行向所在列连其上下界的边. 求有源汇上下界可行流即可. 具体做法可以从汇点向源点连容量为正无穷的边,转成无源汇上下界可行流. 然后可以新建超级源汇,对于一条下界为l,上界为r的边(x,y),从超级源点向y,x向超级汇点连容量为l的边,x向y连容量为r-l的边. 如果那些容量为l的边没满流,则无解. #include <cstdio> #incl

POJ2396&amp;ZOJ1994--Budget【有源汇上下界可行流】

链接:http://poj.org/problem?id=2396 题意:给一个n*m的矩阵,给出每行的总和以及每列的总和,再给出某些位置的最小或最大限制,问是否存在可能的矩阵,如果存在输出一种矩阵信息. 思路:这是一个有源汇的上下界可行流,对于这种题,从汇点连一条弧到源点,容量为INF,这不会影响流量平衡条件,并且此时原图转换为了无源汇的上下界可行流,剩下的做法和无源汇一样. 建图:原图源点src连向每个行顶点,容量为每行的和,每个列顶点连向汇点,容量为每个列顶点的和,行顶点和列顶点间也各有一

算法复习——有源汇上下界可行流(bzoj2396)

题目: Description We are supposed to make a budget proposal for this multi-site competition. The budget proposal is a matrix where the rows represent different kinds of expenses and the columns represent different sites. We had a meeting about this, so

poj2396 Budget(有源汇上下界可行流)

[题目链接] http://poj.org/problem?id=2396 [题意] 知道一个矩阵的行列和,且知道一些格子的限制条件,问一个可行的方案. [思路] 设行为X点,列为Y点,构图:连边(s,Xi,sumXi,sumXi)(Yi,t,sumYi,sumYi)(Xi,Yj,down[i][j],up[i][j]). 则问题就是求一个有源汇点st的上下界可行流. 类似于 无源无汇上下界可行流 ,添加附加源汇点ST,边权转化为up-down,由ST向每个点连边保持流量平衡.然后添加(t,s,

poj2396有源汇上下界可行流

题意:给一些约束条件,要求算能否有可行流,ps:刚开始输入的是每一列和,那么就建一条上下界相同的边,这样满流的时候就一定能保证流量相同了,还有0是该列(行)对另一行每个点都要满足约束条件 解法:先按无源汇上下界可行流建边,然后添加一条从t到s的容量为inf的边,从超级源到超级汇跑一边最大流,看流量是不是等于新加边的流量和,注意这题有可能输入的数据会有锚段,那么我们需要特判一下是否有矛盾出现 还要注意的一点是:我刚开始是用string+cin读入的字符,但是出现了问题,导致我代码下面的那组数据不能

bzoj千题计划158:bzoj2406: 矩阵(有源汇上下界可行流)

http://www.lydsy.com/JudgeOnline/problem.php?id=2406 设矩阵C=A-B 最小化 C 一行或一列和的最大值 整体考虑一行或者一列的和 二分最大值 这样每一行一列的和就有了范围 |Σai-Σbj|<=mid 去掉绝对值 Σai-mid <= Σbi <= Σai+mid 构图: 源点向行连下界为Σai-mid,上界为 Σai+mid 的边 列向汇点连下界为Σai-mid,上界为 Σai+mid 的边 第i行向第j列连下界为L,上界为R的边

poj2396 Budget 有源汇上下界可行流

/** 题目:poj2396 Budget 链接:http://poj.org/problem?id=2396 题意: 给定一个n*m矩阵,矩阵元素未知.已知第1~n行的元素和,第1~m列的元素和.以及元素的一些数据范围. 求一个可行的矩阵. 思路: 联想到以前没有下届的做法,用一个s连接所有的行节点,容量为该行的和,所有的列节点连接t,容量为该列的和. 所有的行节点连接所有的列节点,容量为无穷,然后求s到t的最大流.如果从s出发的弧都是满载的,那么有解. 所有行到所有列的flow为对应的行列位

sgu176 有源汇上下界最小流

题意:有一堆点和边,1起点,n终点,某些边有可能必须满流,要求满足条件的最小流 解法:按原图建边,满流的即上下界都是容量,但是这样按有源汇上下界可行流求出来的可能不是最小流,那么我们需要开始建边的时候不要建从t到s的边,先跑一边从ss到tt的最大流,然后把该边加上再跑一次从ss到tt的最大流,那么从t到s的反向边流过的流量就是原图的最小流,为什么是这样呢,这是因为当我们第一遍跑最大流的时候,此时没有t到s的这条边,那么图中的流量会尽量按其他的边流,当我们第二次跑最大流的时候,流出来的都是第一次中

[zoj] 3229 Shoot the Bullet || 有源汇上下界最大流

zoj 文文要给幻想乡的女孩子们拍照,一共n天,m个女孩子,每天文文至多拍D[i]张照片,每个女孩子总共要被文文至少拍G[i]次.在第i天,文文可以拍c[i]个女孩子,c[i]个女孩子中每个女孩子在当天被拍的次数是[l,r],求最多可以拍多少张照片,以及每天每个可以拍的女孩子被拍了多少张照片. 有源汇上下界最大流. 先跑有源汇上下界可行流,判断是否可行,若可行则此时跑原图中s到t的最大流即为答案. //代码与题解不符-- #include<cstdio> #include<algorit