POJ 1149 PIGS Solution

比较垃圾所以刷的尽是些水题

Description
Mirko works on a pig farm that consists of M locked pig-houses and Mirko can’t unlock any pighouse because he doesn’t have the keys. Customers come to the farm one after another. Each of them has keys to some pig-houses and wants to buy a certain number of pigs.
All data concerning customers planning to visit the farm on that particular day are available to Mirko early in the morning so that he can make a sales-plan in order to maximize the number of pigs sold.
More precisely, the procedure is as following: the customer arrives, opens all pig-houses to which he has the key, Mirko sells a certain number of pigs from all the unlocked pig-houses to him, and, if Mirko wants, he can redistribute the remaining pigs across the unlocked pig-houses.
An unlimited number of pigs can be placed in every pig-house.
Write a program that will find the maximum number of pigs that he can sell on that day.

Solution
首先我们想到把猪圈复制n遍然后建边。
最后在猪圈和人之间建边。
然而点太多Dinic跑不出来。
我们可以考虑缩掉一些点。
1)以后不向人连边的点就没必要复制了。
2)如果对于边c(u,v)=INF,那就可以把u,v给缩了
3)如果有些点他们的流量来源完全相同则可以缩掉。
4)如果有些点他们的流量流出完全相同则可以缩掉。
这样一通缩点以后我们可以惊奇地发现猪圈没了,只有人之间有边。
并且连边的规则是这样的:
对于一个人如果与他相连的猪圈没有被其他人连过则该人与src连一条容量为猪圈中猪的数量的边
不然就和上个连这个猪圈的人连一条容量为INF的边。
然后就可以过掉了。

Edelweiss神牛有一句很有意思的总结:
在面对网络流问题时,如果一时想不出很好的构图方法,不如先构造一个最直观,或者说最“硬来”的模型,然后再用合并节点和边的方式来简化这个模型。经过简化以后,好的构图思路就会涌现出来了。这是解决网络流问题的一个好方法。

[源代码]

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int M=2e4+5;
const int INF=1<<30;
struct Edge{int to,cap,nxt;}edge[M<<1];
int head[M],work[M],tot_edge,A[M],sink,src,last[M],dis[M],n,m;
inline void add_edge(int from,int to,int c){
    edge[tot_edge]=(Edge){to,c,head[from]};
    head[from]=tot_edge++;
    edge[tot_edge]=(Edge){from,0,head[to]};
    head[to]=tot_edge++;
}
bool mark[M];
inline int dfs(int v,int f){
    if(v==sink)return f;
    mark[v]=1;
    for(int &i=work[v];~i;i=edge[i].nxt){
        int to=edge[i].to;
        if(!mark[to]&&dis[to]==dis[v]+1&&edge[i].cap){
            int d=dfs(to,min(f,edge[i].cap));
            if(d){
                edge[i].cap-=d;
                edge[i^1].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
queue<int>que;
inline bool bfs(){
    for(int i=1;i<=n;++i)
        dis[i]=-1;
    que.push(src);
    dis[src]=0;
    for(;!que.empty();){
        int v=que.front();que.pop();
        for(int i=head[v];~i;i=edge[i].nxt){
            int to=edge[i].to;
            if(dis[to]==-1&&edge[i].cap){
                dis[to]=dis[v]+1;
                que.push(to);
            }
        }
    }
    return dis[sink]!=-1;
}
inline int Dinic(){
    int res=0;
    for(;bfs();){
        for(int i=1;i<=n;++i)
            work[i]=head[i];
        for(;;){
            for(int i=1;i<=n;++i)mark[i]=0;
            int f=dfs(src,INF);
            if(!f)break;
            res+=f;
        }
    }
    return res;
}
int main(){
    cin>>m>>n;
    src=n+1,sink=n+2;
    for(int i=1;i<=m;++i)
        scanf("%d",&A[i]),last[i]=src;
    memset(head,-1,sizeof(head));
    for(int i=1,k,s;i<=n;++i){
        scanf("%d",&k);
        for(int j=1,a;j<=k;++j){
            scanf("%d",&a);
            if(last[a]!=src)add_edge(last[a],i,INF);
            else add_edge(last[a],i,A[a]);
            last[a]=i;
        }
        scanf("%d",&s);
        add_edge(i,sink,s);
    }
    n+=2;
    cout<<Dinic()<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值