[poj 1149]PIGS[网络流][Edmonds-Karp][Dinic]

来源:互联网 发布:java 多次使用scanner 编辑:程序博客网 时间:2024/06/07 22:54

【题目大意】

有M个猪圈,每个猪圈里初始时有若干头猪。一开始所有猪圈都是关闭的。依次来了N个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪。每个顾客分别都有他能够买的数量的上限。每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。问总共最多能卖出多少头猪。(1 <= N <= 100, 1 <= M <= 1000)

【建模方法】
不难想象,这个问题的网络模型可以很直观地构造出来。就拿上面的例子来说,可以构造出图1所示的模型(图中凡是没有标数字的边,容量都是∞):
• 三个顾客,就有三轮交易,每一轮分别都有3个猪圈和1个顾客的结点。
• 从源点到第一轮的各个猪圈各有一条边,容量就是各个猪圈里的猪的初始数量。
• 从各个顾客到汇点各有一条边,容量就是各个顾客能买的数量上限。
• 在某一轮中,从该顾客打开的所有猪圈都有一条边连向该顾客,容量都是∞。
• 最后一轮除外,从每一轮的i号猪圈都有一条边连向下一轮的i号猪圈,容量都是∞,表示这一轮剩下的猪可以留到下一轮。
• 最后一轮除外,从每一轮被打开的所有猪圈,到下一轮的同样这些猪圈,两两之间都要连一条边,表示它们之间可以任意流通。

节点合并

规律1. 如果几个结点的流量的来源完全相同,则可以把它们合并成一个。
规律2. 如果几个结点的流量的去向完全相同,则可以把它们合并成一个。
规律3. 如果从点u到点v有一条容量为∞的边,并且点v除了点u以外没有别的流量来源,则可以把这两个结点合并成一个。

让我们从图4中重新总结一下构造这个网络模型的规则:
• 每个顾客分别用一个结点来表示。
• 对于每个猪圈的第一个顾客,从源点向他连一条边,容量就是该猪圈里的猪的初始数量。如果从源点到一名顾客有多条边,则可以把它们合并成一条,容量相加。
• 对于每个猪圈,假设有n个顾客打开过它,则对所有整数i∈[1, n),从该猪圈的第i个顾客向第i + 1个顾客连一条边,容量为∞。
• 从各个顾客到汇点各有一条边,容量是各个顾客能买的数量上限。

<以上来自 [ 网络流建模汇总 ] [ Edelweiss ]    Orz >

相当于首顾客是中转站, 是他可开猪圈中猪数之和.

之后的顾客从首顾客中取出(若涉及首顾客所代表的猪圈)

若有新圏被打开, 则该顾客将成为新圏的中转站,

每个顾客向汇点连一条边, 边权为可买数, 表示此顾客作为中转站可以经手的猪数很多, 但带回家的数目是有限的.


前向星

[Edmonds-Karp]

//156K 0MS#include <cstdio>#include <cstring>#include <queue>#include <algorithm>using namespace std;const int INF = 0x3f3f3f3f;const int MAXN = 105;const int MAXM = 1005;struct pool{    int v,pre,w;}p[MAXN*MAXN*2];int num,head[MAXN],pig[MAXM],flow[MAXN],pre[MAXN],M,N,S,T,opened[MAXM];bool vis[MAXN];queue<int> q;void clear(){    num = 1;    memset(head,0,sizeof(head));    memset(opened,false,sizeof(opened));}void add(int u,int v,int w){    p[++num].v = v, p[num].w = w, p[num].pre = head[u], head[u] = num;}int bfs(){    while(!q.empty())   q.pop();    memset(flow,0,sizeof(flow));    memset(vis,false,sizeof(vis));    vis[S] = true;    flow[S] = INF;    q.push(S);    while(!q.empty())    {        int u = q.front(); q.pop();        for(int tmp=head[u],k;k=p[tmp].v,tmp;tmp=p[tmp].pre)        {            if(!vis[k]&&p[tmp].w>0)///选择可以流的管道进行更新            {                vis[k] = true;                q.push(k);                pre[k] = u;                flow[k] = min(p[tmp].w,flow[u]);            }        }    }    return flow[T];}int Edmonds_Karp(){    int u,f,MaxFlow = 0;    while((f=bfs()))    {        for(u=T;u>S;u=pre[u])        {            for(int tmp=head[pre[u]],k;k=p[tmp].v,tmp;tmp=p[tmp].pre)            {                if(k!=u) continue;                p[tmp].w -= f;                p[tmp^1].w += f;                break;            }        }        MaxFlow += f;    }    return MaxFlow;}int main(){    while(scanf("%d %d",&M,&N)==2)    {        S = 0;        T = N+1;        clear();        for(int i=1;i<=M;i++)            scanf("%d",pig+i);        for(int i=1,a,u;i<=N;i++)        {            scanf("%d",&a);            int MaxWeigh = 0;            while(a--)            {                scanf("%d",&u);                if(!opened[u])                {                    MaxWeigh += pig[u];                    opened[u] = i;                }                else                {                    add(opened[u],i,INF);                    add(i,opened[u],0);                }            }            if(MaxWeigh)                add(S,i,MaxWeigh), add(i,S,0);            scanf("%d",&a);            add(i,T,a);            add(T,i,0);        }        printf("%d\n",Edmonds_Karp());    }}

[Dinic]

//140K 16MS#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>using namespace std;int num, M, N, source, sink;const int INF = 0x3f3f3f3f;const int MAXN = 105;const int MAXM = 1005;int pig[MAXM],opened[MAXM];/* The arc of the flow network.*/struct Pool{    int next, t, c;} edge[MAXM];/* The point of the flow network.*/struct Point{    int son, cur, pre, lim, d;} a[MAXN];int que[MAXN], fi, la;bool vis[MAXN];/* Build the hierarchical graph for the algorithm*/bool build(){    memset(vis, 0, sizeof (vis));    que[fi=la=0]=sink;//reverse    a[sink].d=0, a[sink].cur=a[sink].son, vis[sink]=true;    while (fi<=la)    {        int v=que[fi++];        for (int now=a[v].son, u; u=edge[now].t, now; now=edge[now].next)            if (edge[now^1].c&&!vis[u])//BFS来分层,这里和EK相同            {//倒着BFS的话,当然引用的还是对侧边,即正向边                a[u].d=a[v].d+1;//越向前标号渐大                a[u].cur=a[u].son;//cur指向头                vis[u]=true;//已遍历                que[++la]=u;//入队            }        if (vis[source])return true;//层次图向前已经扩展到源点    }    return false;}/*Use the Dinic algorithm to calculate the max flow.*/int MaxFlow(){    int u, v, now, ret=0;    while (build())    {        a[u=source].lim=INF;        while (true)        {            for (now=a[u].cur; v=edge[now].t, now; now=edge[now].next)//cur优化                if (edge[now].c&&a[u].d==a[v].d+1)break;//找到了一个子节点属于层次图            if (now)            {                a[u].cur=edge[now].next;//下一次从这一条边的下一条边开始dfs                a[v].pre=now;//指向v的边的指针                a[v].lim=min(a[u].lim, edge[now].c);///更新到此处为止流的上限                if ((u=v)==sink)//如果已经找到了一条增广路(走到了尽头)                ///注意这个地方借判断语句, 将u下移, 便于判断为否的时候回到上面进入下一层!                {//进行增广                    do                    {                        edge[a[u].pre].c-=a[sink].lim;                        edge[a[u].pre^1].c+=a[sink].lim;//这两句和Edmonds-Karp是一样的,增广                        u=edge[a[u].pre^1].t;//找前驱~!                    } while (u!=source);                    ret+=a[sink].lim;//增广完毕之后累加新找到的流                }//否则(没走到尽头)继续向下DFS            }            else//没有子节点属于层次图            {                if (u==source)break;//已经退到了源,则已找到最大流,算法结束                a[u].cur=now;//=0,此节点被废弃,子代亦然                u=edge[a[u].pre^1].t;//根据反向边找到前驱~!            }        }    }    return ret;}void clear(){    num = 1;    memset(a, 0, sizeof (a));    memset(opened,false,sizeof(opened));}/* Add an arc to the flow network.*/void add(int x, int y, int z){    edge[++num].t=y;    edge[num].c=z;    edge[num].next=a[x].son;//相当于pool的head数组    a[x].son=num;}int main(){    while(scanf("%d %d",&M,&N)==2)    {        source = 0;        sink = N+1;        clear();        for(int i=1;i<=M;i++)            scanf("%d",pig+i);        for(int i=1,a,u;i<=N;i++)        {            scanf("%d",&a);            int MaxWeigh = 0;            while(a--)            {                scanf("%d",&u);                if(!opened[u])                {                    MaxWeigh += pig[u];                    opened[u] = i;                }                else                {                    add(opened[u],i,INF);                    add(i,opened[u],0);                }            }            if(MaxWeigh)                add(source,i,MaxWeigh), add(i,source,0);            scanf("%d",&a);            add(i,sink,a);            add(sink,i,0);        }        printf("%d\n",MaxFlow());    }}