POJ1149 养猪(最大流)

题面(来源于HLOJ)

题目描述
尼克在一家养猪场工作,这家养猪场共有M间锁起来的猪舍,由于猪舍的钥匙都给了客户,所以尼克没有办法打开这些猪舍,客户们从早上开始一个接一个来购买生猪,他们到达后首先用手中的钥匙打开他所能打开的全部猪舍,然后从中选取他要买的生猪,尼克可以在此期间将打开的猪舍中的猪调整到其它开着的猪舍中,每个猪舍能存放的猪的数量是没有任何限制的。买完猪后客户会将他打开的猪舍关上。
好在尼克事先知道每位客户手中有哪些钥匙,要买多少猪,以及客户到来的先后次序。请你写一个程序,帮助尼克求出最多能卖出多少头生猪。
输入格式
输入文件的第一行包含两个整数M和N,1≤M≤1000,1≤N≤100,M为猪舍的数量,N为客户人数,猪舍的编号为1到M,客户的编号为1到N。
输入文件第二行包含M个空格隔开的整数,依次表示每个猪舍中的生猪数量,每个整数大于等于0,且小于等于1000。
接下来的N行每行表示一位客户的购买信息,第I个客户的购买信息位于第I+2行,
其格式如下:
A K1 K2……KA B
它表示该客户共有A把钥匙,钥匙编号依次为K1 K2……KA,且K1< K2<……< KA,B为该客户要买的生猪的头数。
输出格式
输出文件仅有一行包含一个整数,表示尼克最多能卖出的生猪的头数。

题解

经典的网络流问题。
我们先想一想,猪的流动。总体流动方向是从猪圈流到客户手中,所以我们可以建立一个超级源点S,并连接S到所有猪圈的边,容量为猪圈初始猪的数量;建立一个超级汇点T,并连接所有客户到T的边,容量为客户希望买到的猪的数量。
继续思考,每一个客户打开猪圈后,这些猪可以乱跑,所以下一位客户只要在上一个客户打开的猪圈里任意打开一个,就可以买到上一位客户打开的猪圈里所有的猪。
所以我们可以 继续连边:对于任意一个猪圈,向第一个打开它的客户连一条INF的边,对于任意一个猪圈,每一个客户都向紧接着打开该猪圈的下一个客户连一条INF的边。
这样做一遍dinic最大流就行了。

要命的梗

数组越界果然什么都会出来。我因为数组越界,先wa后tle。。

code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int num=0;char c=' ';bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
        if(c=='-')
            flag=false;
    for(;c>='0'&&c<='9';num=(num<<3)+(num<<1)+c-48,c=getchar());
    return flag ? num : -num;
}
namespace graph{
    const int INF=0x7FFFFFFF;
    const int maxn=5200,maxm=1020;
    struct node{
        int y,val,next;
    }a[(maxn+maxm+maxm*maxm)<<1];
    int head[maxm],top=0,m,n;
    void insert(int x,int y,int v){
        a[top].y=y;
        a[top].val=v;
        a[top].next=head[x];
        head[x]=top++;
    }
    int pre[maxm],s,t;
    void init(){
        memset(head,-1,sizeof head);
        m=read();n=read();
        s=0;t=n+m+1;
        for(int i=1;i<=m;i++){
            int x=read();
            insert(s,i,x);
            insert(i,s,0);
        }//源点向所有猪圈连边 
        for(int i=1;i<=n;i++){
            int num=read();
            for(int j=1;j<=num;j++){
                int x=read();
                if(pre[x])insert(pre[x],i+m,INF),insert(i+m,pre[x],0);
                else insert(x,i+m,INF),insert(i+m,x,0);
                //猪圈向第一个客户连边
                //每一客户向下一个紧接着打开该猪圈的客户连边 
                pre[x]=i+m;
            }
            int want=read();
            insert(i+m,t,want);
            insert(t,i+m,0);
            //客户向汇点连边 
        }
    }
}using namespace graph;

namespace MAX_flow{//下面就是一个裸最大流
//带当前弧优化的dinic算法 
    int d[maxn],cur[maxn];
    bool bfs(){
        memset(d,0,sizeof d);
        queue<int>q;
        q.push(s);
        d[s]=1;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=a[i].next){
                int v=a[i].y;
                if(a[i].val==0||d[v])continue;
                d[v]=d[u]+1;
                q.push(v);
                if(v==t)return true;
            }
        }
        return false;
    }
    int dfs(int x,int flow){
        if(x==t)return flow;
        for(int &i=cur[x];i!=-1;i=a[i].next){//当前弧优化 
            int y=a[i].y;
            if(a[i].val==0||d[y]!=d[x]+1)continue;
            int k=dfs(y,min(flow,a[i].val));
            if(k>0){
                a[i].val-=k;
                a[i^1].val+=k;
                return k;
            }
        }
        return 0;
    }
}using namespace MAX_flow;
int main(){
    init();
    int ans=0;
    while(bfs()){
        for(int i=s;i<=t;i++)
            cur[i]=head[i];
        while(int flow=dfs(0,INF))
            ans+=flow;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值