POJ 1149 最大流

最初读完这道题目之后,完全没有什么想法,也根本不会想到用最大流的做法。因为题目的条件似乎很多变:每个顾客打开自己的猪笼后,可以随时变换里面猪的数量,这个条件就给思考上带来了一定难度。

后来到网上看了一下题解,才发现建图是关键。具体建图的思路和过程,详见:http://imlazy.ycool.com/post.2059102.html。只要你知道最大流是干什么的,这个题解肯定能够看懂。


总结:

1、建图:

如果一道题目看出来是最大流,可能一开始建图会很麻烦,但是要在麻烦的建图中,找到简化建图的方案。就像上面的题解一样,简化到最大流算法可以接受的复杂度范围内,然后再从简化的建图中,去找建图的规律。这一步也是做出这道题目的关键。


2、最大流算法中的细节问题:

(1)、在初始化建图的时候,一定要记得把源点和汇点先放在变量里面,这样可以省去后面如果发现问题,改变源点和汇点的麻烦。

(2)、我的sap模板中,st,ed就不用说了,cnt是个数+1,因为所有点是从0开始计算的。

(3)、sap的参数尽量不要用n,用cnt吧。

(4)、sap函数中的第一个循环中的i,不要用int。


Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 111;
const int maxm = maxn * maxn;
const int inf = 1 << 29;
int n, m;

struct node
{
    int c, to, next;
}edge[maxm];

int head[maxn];
int num[maxm];
bool visit[maxm];
int sm;

void add_edge(int u, int v, int cap)
{
    edge[sm].to = v, edge[sm].c = cap, edge[sm].next = head[u];
    head[u] = sm ++;
    edge[sm].to = u, edge[sm].c = 0, edge[sm].next = head[v];
    head[v] = sm ++;
}

int sap(int st, int ed, int cnt)
{
    int numh[maxn], h[maxn], curedge[maxn], pre[maxn];
    int cur_flow, ans = 0, u, tmp, neck, i;
    memset(h, 0, sizeof(h));
    memset(numh, 0, sizeof(numh));
    memset(pre, -1, sizeof(pre));
    for(i = 0; i <= cnt; i ++)
        curedge[i] = head[i];
    numh[0] = cnt;
    u = st;
    while(h[st] < cnt)
    {
        if(u == ed)
        {
            cur_flow = inf;
            for(i = st; i != ed; i = edge[curedge[i]].to)
                if(cur_flow > edge[curedge[i]].c)
                {
                    neck = i;
                    cur_flow = edge[curedge[i]].c;
                }
            for(i = st; i != ed; i = edge[curedge[i]].to)
            {
                tmp = curedge[i];
                edge[tmp].c -= cur_flow;
                edge[tmp^1].c += cur_flow;
            }
            ans += cur_flow;
            u = neck;
        }
        for(i = curedge[u]; i != -1; i = edge[i].next)
            if(edge[i].c && h[u] == h[edge[i].to] + 1)
                break;
        if(i != -1)
        {
            curedge[u] = i;
            pre[edge[i].to] = u;
            u = edge[i].to;
        }
        else
        {
            if(--numh[h[u]] == 0)
                break;
            curedge[u] = head[u];
            for(tmp = cnt, i = head[u]; i != -1; i = edge[i].next)
                if(edge[i].c)
                    tmp = min(tmp, h[edge[i].to]);
            h[u] = tmp + 1;
            ++ numh[h[u]];
            if(u != st)
                u = pre[u];
        }
    }
    return ans;
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        memset(head, -1, sizeof(head));
        memset(visit, false, sizeof(visit));
        int pre[maxm];
        sm = 0;
        int st = 0, ed = m + 1;
        for(int i = 1; i <= n; i ++)
            scanf("%d", &num[i]);
        for(int i = 1; i <= m; i ++)
        {
            int k, key;
            scanf("%d", &k);
            while(k --)
            {
                scanf("%d", &key);
                if(!visit[key])
                {
                    add_edge(st, i, num[key]);
                    visit[key] = true;
                }
                else
                    add_edge(pre[key], i, inf);
                pre[key] = i;
            }
            int amount;
            scanf("%d", &amount);
            add_edge(i, ed, amount);
        }
        int res = sap(st, ed, ed + 1);
        printf("%d\n", res);
    }
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值