【POJ 2396】有源汇上下界可行流

1月14号的时候看Fancy学姐的博客学网络流,写的很好,很容易懂。然后就开始学有源汇上下界可行流,方法很简单,就添一条t到s上下界为0, INT_MAX的边然后按无源汇上下界可行流的做就行了【但是具体为什么这样可以我还不是很懂。。。。】

于是开始做题,就这道POJ2396。
我简单说下题意:给你一个矩阵,每行的和,每列的和,和一些限制条件,让你输出可行的矩阵。

Fancy说的方法是对于每行和每列分别建一个点,ss 与每行的点连 [a[i],a[i]][a[i],a[i]] 的边,每列的点与 tt连 [b[i],b[i]][b[i],b[i]] 的边,中间连的边为点(i,j)(i,j) 的限制 我尝试着胡写但是不可行。网上其他的题解也大都是这个思路,而且代码还都是数组的,我写邻接表啊根本没法抄qnq

这时候我看了一下午已经到了晚上,然后Menci看了,给我说了一下具体的解法,是这样的:我们把一行当做一个点,一列当做一个点,建一个源点s往每个行点上连边,上下界都为行和;每个列点往汇点t上连边,上下界为列和。
我们想象再为矩阵上的每一个数建一个点,约束【比如说2 3 > 2 第二行第三列上的那一个店要大于2】就是那一个行点【2】,向那个点连边,那个点再向有约束的另一个点连边,这个点再连向列点(如果是一整行或者一整列的约束把它拆分成每个点来做)。但是这样的话就相当于从行点直接连向列点,因为流量平衡,所以我们就可以省掉每一个数的点,只需要n + m个点就可以了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <climits>
using namespace std;

const int MAXN = 400 + 5;
const int MAXM = (MAXN * (MAXN - 1)) / 2;
const int MAXr = 200 + 5, MAXc = 20 + 5;

int minn[MAXr][MAXc], maxx[MAXr][MAXc];
int a[MAXr], b[MAXc];
int n, m;

struct Node {
    struct Edge *lastE, *curE;
    int level, extraIn;
} N[MAXN];

struct Edge {
    Node *from, *to;
    int flow, cap, low;
    Edge *next, *revE;

    Edge(Node *from, Node *to, int cap) : from(from), to(to), flow(0), cap(cap), next(from->lastE) {}
} *E[MAXr][MAXc];

struct Dinic {
    bool makeLevelGraph(Node *s, Node *t, int n)
    {
        for (int i = 0; i < n; i++)
        {
            N[i].level = 0;
            N[i].curE = N[i].lastE;
        }

        queue<Node *> q;
        q.push(s);

        s->level = 1;

        while (!q.empty())
        {
            Node *v = q.front();
            q.pop();

            for (Edge *e = v->lastE; e; e = e->next)
            {
                if (e->flow < e->cap && !e->to->level)
                {
                    e->to->level = v->level + 1;
                    //printf("%d !\n", e->to->level);

                    if (e->to == t)
                    {
                        return true;
                    }
                    else
                    {
                        q.push(e->to);
                    }
                }
            }
        }

        return false;
    }

    int findPath(Node *s, Node *t, int limit = INT_MAX)
    {
        if (s == t) return limit;

        for (Edge *&e = s->curE; e; e = e->next)
        {
            if (e->flow < e->cap && e->to->level == s->level + 1)
            {
                int flow = findPath(e->to, t, min(limit, e->cap - e->flow));

                if (flow)
                {
                    e->flow += flow;
                    e->revE->flow -= flow;
                    return flow;
                }
            }
        }

        return 0;
    }

    int operator()(int s, int t, int n)
    {
        int res = 0;
        while (makeLevelGraph(&N[s], &N[t], n))
        {
            int flow;
            while ((flow = findPath(&N[s], &N[t])) > 0)
            {
                res += flow;
                //printf("%d $ %d  ", flow, res);
            }
            //printf("\n");
        }
        return res;
    }
} dinic;

inline Edge *addEdge(int u, int v, int cap)
{
    N[u].lastE = new Edge(&N[u], &N[v], cap);
    N[v].lastE = new Edge(&N[v], &N[u], 0);

    N[u].lastE->revE = N[v].lastE;
    N[v].lastE->revE = N[u].lastE;

    return N[u].lastE;
}

inline Edge *addEdge(int u, int v, int low, int high)
{
    int cap = high - low;
    Edge *e = addEdge(u, v, cap);

    e->low = low;

    N[u].extraIn -= low;
    N[v].extraIn += low;

    return e;
}

inline void change(int r, int c, char fu, int d)
{
    if (fu == '>')
    {
        minn[r][c] = max(minn[r][c], d + 1);
    }
    else if (fu == '<')
    {
        maxx[r][c] = min(maxx[r][c], d - 1);
    }
    else if (fu == '=')
    {
        minn[r][c] = max(minn[r][c], d);
        maxx[r][c] = min(maxx[r][c], d);
    }

    // if (minn[r][c] > maxx[r][c])
    // {
    //     return false;
    // }
    //
    // return true;
}

inline bool flow(int s, int t, int n)
{
    int S = 0, T = n + 1;

    addEdge(t, s, INT_MAX);

    int sum = 0;

    for (int i = 1; i <= n; i++)
    {
        if (N[i].extraIn > 0)
        {
            addEdge(S, i, N[i].extraIn);
            sum += N[i].extraIn;
        }
        else if (N[i].extraIn < 0)
        {
            addEdge(i, T, -N[i].extraIn);
        }
    }

    return dinic(S, T, n + 2) == sum;
    // int flow = dinic(S, T, n + 2);
    // if (flow < sum) return false;
    // return true;
}

inline bool solve()
{
    int s = n + m + 1, t = n + m + 2;
    //addEdge(t, s, 0, INT_MAX);

    for (int i = 1; i <= n; i++)
    {
        addEdge(s, i, a[i], a[i]);
    }

    for (int i = 1; i <= m; i++)
    {
        addEdge(i + n, t, b[i], b[i]);
    }

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (minn[i][j] > maxx[i][j]) return false;
            E[i][j] = addEdge(i, j + n, minn[i][j], maxx[i][j]);
            //printf("%d~ %d  ", minn[i][j], maxx[i][j]);
        }
        //printf("\n");
    }
    //printf("\n");

    return flow(s, t, n + m + 2);
}

int main()
{
    int num;
    scanf("%d", &num);

    while (num--)
    {

        scanf("%d %d", &n, &m);

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                minn[i][j] = 0;
                maxx[i][j] = INT_MAX;
            }
        }

        // memset(a, 0, sizeof(a));
        // memset(b, 0, sizeof(b));

        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }

        for (int i = 1; i <= m; i++)
        {
            scanf("%d", &b[i]);
        }

        int k;
        scanf("%d", &k);

        //bool flag = true;

        //int cnt = n

        while (k--)
        {
            //scanf("%d %d", &n, &m);


            int r, c, d;
            char fu;

            scanf("%d %d %c %d", &r, &c, &fu, &d);
            //先存起来,最后再加

            //c = c + n;

            if (r == 0 && c != 0)
            {
                for (int i = 1; i <= n; i++)
                {
                    change(i, c, fu, d);
                    // if (change(i, c, fu, d) == false)
                    // {
                    //     flag = false;
                    // }
                }
            }
            else if (c == 0 && r != 0)
            {
                for (int i = 1; i <= m; i++)
                {
                    change(r, i, fu, d);
                    // if (change(r, i, fu, d) == false)
                    // {
                    //     flag = false;
                    // }
                }
            }
            else if (r == 0 && c == 0)
            {
                for (int i = 1; i <= n; i++)
                {
                    for (int j = 1; j <= m; j++)
                    {
                        change(i, j, fu, d);
                        // if (change(i, j, fu, d) == false)
                        // {
                        //     flag = false;
                        // }
                    }
                }
            }
            else
            {
                change(r, c, fu, d);
                //if (change(r, c, fu, d) == false)
                // {
                //     flag = false;
                // }
            }
        }

        // if (flag == false)
        // {
        //     printf("IMPOSSIBLE\n");
        //     if (num != 0)
        //     {
        //         printf("\n");
        //     }
        //     continue;
        // }

        bool flag = solve();

        if (flag)
        {
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= m; j++)
                {
                    printf("%d ", E[i][j]->flow + E[i][j]->low);
                }
                printf("\n");
            }

            if (num != 0)
            {
                printf("\n");
            }
        }
        else
        {
            printf("IMPOSSIBLE\n");

            if (num != 0)
            {
                printf("\n");
            }
        }

        for (int i = 0; i <= n + m + 4; i++)
        {
            for (Edge *&e = N[i].lastE, *next; e; next = e->next, delete e, e = next);
            N[i].level = N[i].extraIn = 0;
        }
        // int s = n + 2, t = n + 3;
        // for (int i = )
    }
    return 0;
}

我是抄Menci的板写的邻接链表实现的Dinic
注释掉的有一些是中间的调试信息

大的框架14号晚上就写完了,然后样例都过不了(微笑)16号又调了一晚自习,今天整整一上午,都有点恶心qnq期间略过对着Menci的程序肉眼Debug不提,主要我出的错有一下几个:

1、约束那里是先用一个二维数组把上下界存起来,方便修改,>和<的时候要minn[i][j] = max(minn[i][j], d + 1)'同样maxx[i][j] = min(maxx[i][j], d - 1) 因为我们后来用的时候是大于d的整数,也就是等于d + 1;

2、一开始是给maxx数组赋初值INT_MAX,给minn数组赋初值0,而不是INT_MIN, 因为流量的下界是0,而不能是负的;

3、这里点的编号是1~n + m的,但边是用二维数组存的(这里只存点之间的边就行,其他的边可以不用存在E这个指针数组里),所以循环的时候一定要注意是1~n,1~m还是1~n,n + 1~n + m【我检查了辣么多遍函数结果主函数写错了。。。。】

4、需要的边用一个二维指针数组存起来比较方便,我觉得用结构体不太好实现(可能是我辣鸡(:з」∠)

这道题算法其实还比较好想(虽然大概我也想不出来),但是实现的时候有点恶心(还是我太弱。。。dalao很快就A啦)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值