【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啦)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章