PIGS POJ - 1149网络流(最短增广路---广搜) + 建图

题意:
第一行输入m和n,m是猪圈的数量,n是顾客的数量,下面n行 第 i+1行表示第i个顾客 , 输入第一个数字表示有几把猪圈的钥匙,后面输入对应的猪圈,最后一个数字输入顾客想买几头猪.
建图:
设一个源点 s 和一个汇点 t,s 连接猪圈被第一个打开的顾客,权值为猪圈的猪数量,t 与顾客相连,权值为顾客想要的猪的数量,因为题上说迈克会根据下一个顾客的需求来将猪转移到合适的猪圈,所以顾客与同一猪圈排队的后面紧邻的顾客相连,权值为正无穷。
想一下感觉挺对的,s点有无数头猪,但是边的权值为猪圈的数量,说明顾客只能拿这么多,后面紧邻的顾客最然边权是无穷大,但被前面的顾客限制着,虽然顾客可以拿到猪圈里的猪,但是能带到 t 汇点的最多是自己想要的数量,所以求s到t的最大流。
注意:
书上的代码还要开一个数组记录残留网络什么的,我觉得太繁琐,直接在一个 e [ ] [ ]数组里面操作就好。注释的代码是自己建图错误了
关于网络流数组操作
为什么要正向边减去 流,反向边加上 流?
我觉得就像水流一样,从 s 流到 t 后,再沿着原路返回(进行加减操作),如果这个残余网络还有路径可以流,就根据这个残余网络找s到t的路径。

#include<stdio.h>
#include<vector>
#include<string.h>
#include<queue>
using namespace std;
int s,t,n,m,pig[1010],p[120],a[1010],v[1010][120],e[120][120],inf=0x3f3f3f3f;
int bfs()//广搜去找增广路,记录路径
{
    int book[110];
    for(int i=0; i<=t; i++)
        book[i]=0,a[i]=-1;
    queue<int>q;
    q.push(s);
    book[s]=1;
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        if(k==t)
            return 1;
        for(int i=1; i<=t; i++)
        {
            if(e[k][i]&&!book[i])
            {
                a[i]=k;
                book[i]=1;
                q.push(i);
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(v,0,sizeof(v));
        memset(e,0,sizeof(e));
        s=m+1,t=m+2;
        int t1,t2;
        for(int i=1; i<=n; i++)
            scanf("%d",&pig[i]);//几号猪舍有几头猪
        int last[1120],num,k;
        memset(last,0,sizeof(last));//记录几号猪舍前面一个顾客是谁
        for(int i=1;i<=m;i++)//开始建图
        {
            scanf("%d",&num);
            for(int j=0;j<num;j++)
            {
                scanf("%d",&k);
                if(last[k]==0)
                    e[s][i]+=pig[k];
                else
                    e[last[k]][i]=inf;
                last[k]=i;
            }
            scanf("%d",&e[i][t]);
        }//建图完成
//        for(int i=1;i<=t;i++)
//        {
//            for(int j=1;j<=t;j++)
//                printf("%d ",e[i][j]);
//            printf("\n");
//        }
//        for(int i=1; i<=m; i++)
//        {
//            scanf("%d",&t1);
//            int k;
//            for(k=1; k<=m; k++)
//                if(v[i][k]==0)
//                    break;
//            for(int j=1; j<=t1; j++)
//            {
//                scanf("%d",&t2);
//                v[i][k++]=t2;
//            }
//            scanf("%d",&p[i]);//几号顾客买几只猪
//        }
//

//        for(int i=1; i<=n; i++) //几号猪舍
//        {
//            for(int j=1; j<=m&&v[i][j]; j++) //第几位顾客
//            {
//                if(j==1)
//                    e[s][v[i][j]]+=pig[i];
//                else
//                    e[v[i][j-1]][v[i][j]]=inf;
//            }
//        }
//        for(int i=1; i<=m; i++)
//            e[i][t]=p[i];//建图完成

        int sum=0;
        while(1)
        {
            int h=t,minn=inf;
            if(!bfs())//直到找不到增广路为止
                break;
            while(a[h]!=-1)//找到所走路径上最短的边
            {
                if(e[a[h]][h]<minn)
                    minn=e[a[h]][h];
                h=a[h];
            }
            sum+=minn;
            h=t;
            while(a[h]!=-1)//减去那个最短的边,再反向加上
            {
                e[a[h]][h]-=minn;
                e[h][a[h]]+=minn;
                h=a[h];
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值