Poj 3281 && Regional Chengdu Food(Dicnic)

网络流最大流的优化算法Dicnic,每一步对原图进行分层,然后用DFS求增广路。时间复杂度是O(n^2*m) 。

Poj 3281 和 9.16号成都regional网络赛food那道题,都是很好的模板题。。

以food那题为例,这样建图:设一个源点,每一种food为一层,每个人分成两部分,people1 people2两层,每种饮料是一层,然后是一个汇点。

每一种食物的个数,即为源点到那种food的流量;

饮料同理。

每一个人只消耗一种一个食物和一种一个饮料,因此每种食物(或饮料)与人的连线,流量为1 

people1与people2的间的流量为1

模板放这儿先。

POJ 3281

#include<stdio.h>
#include<string.h>
const int EDGE_NUM = 20001;//边数
const int POINT_NUM = 501;//点数
struct edge
{
    int v;//点
    int next;//下一边
    int value;//当前边流量
} edge[2*EDGE_NUM]; //边信息,以邻接表形式存储

int p[POINT_NUM];//p[i]记录最后一条以i为起点的边的id,即以i为起点的最后一条边为edge[p[i]],而edge[p[i]].next则为以i为起点的倒数第二条边,以此类推
int level[POINT_NUM];//level[i]记录i点的层次
int que[POINT_NUM],out[POINT_NUM];//辅助数组
int edgeNumber;

void init()
{
    edgeNumber = 0;
    memset(p,-1,sizeof(p));
}

inline void addEdge(int from,int to,int value)//添加边,以邻接表形式存储
{
    edge[edgeNumber].v = to;
    edge[edgeNumber].value = value;
    edge[edgeNumber].next = p[from];
    p[from] = edgeNumber++;
}

int Dinic(int source,int sink,int n)
{
    int i,maxFlow = 0;
    while(true)
    {
        int head,tail;
        for(i=0; i<n; i++)level[i] = 0;

        level[source] = 1;//源点为第一层
        head = 0;
        tail = 0;
        que[0] = source;//que这里当队里使用

        while(head<=tail)//BFS该剩余图,计算每个可达点层次
        {
            int cur = que[head++];
            for(i=p[cur]; i!=-1; i=edge[i].next)
            {
                if(edge[i].value>0&&level[edge[i].v]==0)
                {
                    level[edge[i].v] = level[cur]+1;
                    que[++tail] = edge[i].v;
                }
            }
        }


        if(level[sink]==0)break;//不存在增广路

        for(i=0; i<n; i++)out[i]=p[i]; //out[i]动态记录可用边

        int q = -1;//q为已经搜索到的点的个数,que存放途径边信息

        while(true)//DFS剩余图,查找增广路
        {
            if(q<0)//当前路为空
            {
                int cur = out[source];
                for(; cur!=-1; cur=edge[cur].next) //查找第一条边
                {
                    if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==2)//合法第一条边必须满足:1.流量大于0;2.终点有可用边 3:终点层次为2
                        break;
                }

                if(cur==-1)break;//找不到第二层,当前剩余图已经没有增广路

                que[++q]=cur;//存入第一条边id
                out[source]=edge[cur].next;
            }

            int curnode = edge[que[q]].v;//当前路的终点

            if(curnode==sink)//找到一条增广路
            {
                int thisflow = edge[que[0]].value;//thisflow为当前增广路的流量
                int index = 0;//标记最小流量边的id
                for(i=1; i<=q; i++)
                {
                    if(thisflow>edge[que[i]].value)
                    {
                        thisflow=edge[que[i]].value;
                        index = i;
                    }
                }

                maxFlow+=thisflow;

                for(i=0; i<=q; i++)
                {
                    edge[que[i]].value-=thisflow;
                    edge[que[i]^1].value+=thisflow;//与其方向相反的边
                }

                q = index-1;//查找下一条增广路时可直接使用当前路的前q条边

            }
            else//尚未找到汇点
            {
                int cur = out[curnode];
                for(; cur!=-1; cur=edge[cur].next)
                {
                    if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==level[curnode]+1)
                        break;
                }
                if(cur==-1)//没有下一条路
                {
                    out[curnode]=-1;//标记当前点的可达边为0
                    q--;
                }
                else
                {
                    que[++q]=cur;
                    out[curnode]=edge[cur].next;//下一次搜索时可达边从edge[cur].next开始查找
                }
            }
        }


    }

    return maxFlow;
}

int main()
{
    int Nn,Ff,Dd;
    while(scanf("%d%d%d",&Nn,&Ff,&Dd)!=EOF)
    {
        init();
        int foodstart = 1;
        int cow1 = Ff+2;
        int cow2 = cow1+Nn+1;
        int drinkstart = cow2+Nn+1;
        int end = drinkstart+Dd+1;

        int i;
        for(i=0; i<Nn; i++) //添加牛边
        {
            addEdge(cow1+i,cow2+i,1);
            addEdge(cow2+i,cow1+i,0);
        }

        for(i=0; i<Ff; i++) //添加食物边
        {
            addEdge(0,foodstart+i,1);       
            addEdge(foodstart+i,0,0);
        }

        for(i=0; i<Dd; i++) //添加饮料
        {
            addEdge(drinkstart+i,end,1); 
            addEdge(end,drinkstart+i,0);
        }

        for(i=0; i<Nn; i++)
        {
            int f,d;
            scanf("%d%d",&f,&d);
            int x;
            while(f--)
            {
                scanf("%d",&x);
                x--;
                addEdge(foodstart+x,cow1+i,1);
                addEdge(cow1+i,foodstart+x,0);
            }
            while(d--)
            {
                scanf("%d",&x);
                x--;
                addEdge(cow2+i,drinkstart+x,1);
                addEdge(drinkstart+x,cow2+i,0);
            }
        }


        printf("%d\n",Dinic(0,end,end+1));
    }
    return 0;
}

成都regional网络赛

#include<cstdio>
#include<cstring>
#include <memory.h>
const int EDGE_NUM = 100001;//边数
const int POINT_NUM = 250;//点数
struct edge
{
    int v;//点
    int next;//下一边
    int value;//当前边流量
} edge[2*EDGE_NUM]; //边信息,以邻接表形式存储

int p[POINT_NUM];//p[i]记录最后一条以i为起点的边的id,即以i为起点的最后一条边为edge[p[i]],而edge[p[i]].next则为以i为起点的倒数第二条边,以此类推
int level[POINT_NUM];//level[i]记录i点的层次
int que[POINT_NUM],out[POINT_NUM];//辅助数组
int edgeNumber;
int food[250];
int drink[250];
void init()
{
    edgeNumber = 0;
    memset(p,-1,sizeof(p));
}
void addEdge(int from,int to,int value)//添加边,以邻接表形式存储
{
    edge[edgeNumber].v = to;
    edge[edgeNumber].value = value;
    edge[edgeNumber].next = p[from];
    p[from] = edgeNumber++;
}

int Dinic(int source,int sink,int n)
{
    int i,maxFlow = 0;
    while(true)
    {
        int head,tail;
        for(i=0; i<n; i++)level[i] = 0;

        level[source] = 1;//源点为第一层
        head = 0;
        tail = 0;
        que[0] = source;//que这里当队里使用

        while(head<=tail)//BFS该剩余图,计算每个可达点层次
        {
            int cur = que[head++];
            for(i=p[cur]; i!=-1; i=edge[i].next)
            {
                if(edge[i].value>0&&level[edge[i].v]==0)
                {
                    level[edge[i].v] = level[cur]+1;
                    que[++tail] = edge[i].v;
                }
            }
        }


        if(level[sink]==0)break;//不存在增广路

        for(i=0; i<n; i++)out[i]=p[i]; //out[i]动态记录可用边

        int q = -1;//q为已经搜索到的点的个数,que存放途径边信息

        while(true)//DFS剩余图,查找增广路
        {
            if(q<0)//当前路为空
            {
                int cur = out[source];
                for(; cur!=-1; cur=edge[cur].next) //查找第一条边
                {
                    if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==2)//合法第一条边必须满足:1.流量大于0;2.终点有可用边 3:终点层次为2
                        break;
                }

                if(cur==-1)break;//找不到第二层,当前剩余图已经没有增广路

                que[++q]=cur;//存入第一条边id
                out[source]=edge[cur].next;
            }

            int curnode = edge[que[q]].v;//当前路的终点

            if(curnode==sink)//找到一条增广路
            {
                int thisflow = edge[que[0]].value;//thisflow为当前增广路的流量
                int index = 0;//标记最小流量边的id
                for(i=1; i<=q; i++)
                {
                    if(thisflow>edge[que[i]].value)
                    {
                        thisflow=edge[que[i]].value;
                        index = i;
                    }
                }

                maxFlow+=thisflow;

                for(i=0; i<=q; i++)
                {
                    edge[que[i]].value-=thisflow;
                    edge[que[i]^1].value+=thisflow;//与其方向相反的边
                }

                q = index-1;//查找下一条增广路时可直接使用当前路的前q条边

            }
            else//尚未找到汇点
            {
                int cur = out[curnode];
                for(; cur!=-1; cur=edge[cur].next)
                {
                    if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==level[curnode]+1)
                        break;
                }
                if(cur==-1)//没有下一条路
                {
                    out[curnode]=-1;//标记当前点的可达边为0
                    q--;
                }
                else
                {
                    que[++q]=cur;
                    out[curnode]=edge[cur].next;//下一次搜索时可达边从edge[cur].next开始查找
                }
            }
        }


    }

    return maxFlow;
}

int main()
{
    int Nn,Ff,Dd;
    while(scanf("%d%d%d",&Nn,&Ff,&Dd)!=EOF)
    {
        init();
        int foodstart = 1;
        int cow1 = Ff+1;
        int cow2 = cow1+Nn+1;
        int drinkstart = cow2+Nn+1;
        int end = drinkstart+Dd+1;
        for(int i=1;i<=Ff;i++)scanf("%d",&food[i]);
        for(int i=1;i<=Dd;i++)scanf("%d",&drink[i]);
        int i;
        for(i=0; i<Nn; i++) //添加牛边
        {
            addEdge(cow1+i,cow2+i,1);
            addEdge(cow2+i,cow1+i,0);
        }

        for(i=0; i<Ff; i++) //添加食物边
        {
            addEdge(0,foodstart+i,food[i+1]); //每一种食物路径的流量。

            addEdge(foodstart+i,0,0);
        }

        for(i=0; i<Dd; i++) //添加饮料
        {
            addEdge(drinkstart+i,end,drink[i+1]);
            addEdge(end,drinkstart+i,0);
        }
        char t;
        for(i=0; i<Nn; i++)
        {
            getchar();
            for(int j=0; j<Ff; j++)
            {
                scanf("%c",&t);
                if(t=='Y')
                {
                    addEdge(foodstart+j,cow1+i,1);
                    addEdge(cow1+i,foodstart+j,0);
                }
            }
        }
        for(i=0; i<Nn; i++)
        {
            getchar();
            for(int j=0; j<Dd; j++)
            {
                scanf("%c",&t);
                if(t=='Y')
                {
                    addEdge(cow2+i,drinkstart+j,1);
                    addEdge(drinkstart+j,cow2+i,0);
                }
            }
        }

        printf("%d\n",Dinic(0,end,end+1));
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值