• POJ 3281 Dining【Dinic】


     POJ 3281  Dining

     核心算法:网络最大流

    大意:
     有n头牛,F种食物,D种饮料,
     第i头牛喜欢fi种食物,di种饮料,编号分别为。。。
     
      已知一头牛最多能吃一种食物和一种饮料,每种饮料
      或食物最多能被一头牛吃,求以上条件下,最多能有多少头
      牛能吃到他所喜爱的食物和饮料

    建立模型:
        建立网络流模型:
     1.对每种食物建立从源点指向它的一条边,流量为1
        2.在牛与它所喜爱的食物间建立一条边,流量为1
     3.在牛与它所喜欢的饮料间建立一条边,流量为1
     4.对每种饮料建立一条指向汇点的边,流量为1
     5.在上面的基础上,将牛拆点,在拆开的点间建立一条流量为1的边
       在以上条件下,从源点到汇点的最大流即为答案

      模型的分析:
      条件1使得满足每种食物有且只有一个,条件4类似
      条件2使得每头牛只能选择自己喜欢的食物,条件3类似
      条件5使得每头牛最多只能选择一种饮料和食物

    这里  最大流使用的是dinic算法。。

    代码
    #include<stdio.h>
    #include
    <string.h>
    constint EDGE_NUM =20001;//边数
    constint 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));
    }
    return0;
    }
  • 相关阅读:
    java反射系七之动态代理
    java反射系列六之调用属性与方法
    java反射系列五之获取类的完整结构
    java反射系列四之创建运行时类的对象
    java反射系列三之类加载器
    java反射系列二
    java反射系列一
    数据类型
    类的加载、连接和初始化
    算法
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1947445.html
Copyright © 2020-2023  润新知