poj1149 pigs

poj1149
题意:有m猪圈,n个买猪的人,每个买猪的能从k个猪圈中最多拿a只猪,然后着k个猪圈的猪可以..互换位置,问最多能卖出多少只猪
(虽然我很好奇…卖猪的人事很多诶

这题真的可以说很妙了,构图经典!这个讲的贼好!!!
简单来说…最开始可以想到把猪圈分成n层,每层代表来一个人以后的,即源点向最初的每个猪圈连边(值为猪的个数);每次的猪圈要是被打开了就向打开它的那个人连边然后这个人再向下一阶段这个猪圈连边,没打开就直接连到下一阶段这个猪圈(值为inf);再连上n个人和汇点(值为他最多能买多少)。
然后我们发现,如果这个猪圈在这一阶段没有被打开,那我们根本不需要把它再连一条边到下一阶段的这个猪圈,就保留这个节点就好啦。这时我们发现,我们为什么能删去了这个边,因为它的值是inf,删去它不会对别的有任何影响(好像是废话)。而我们再去看这个图,发现有很多边的值都是inf(很神奇吧),而且都是连着的!如果一个有用的点连向某一个点,这个点又连向另一个有用的点,而这两条边的值都是inf,且中间这个点没有连过别的边,那么,这个点根本没用啊,就..能够多走几步…脱时间!
因此我们再去看这个图,发现这些猪圈的点都没有用了!只有人是有用的,那么图的点数一下子..就从 105 10 5 变成了102!!!
所以,我们就去找那些第一次打开第i个猪圈的人,然后把源点向这个人连边,值为第i个猪圈中猪的个数;然后每个人之间也要连边,也就是打开第i个猪圈的前后两个人,那么就把这两个人连边,值为inf;最后就把n个人向汇点连边。

#include <cstdio>
#include <cstring>
#include <queue> 
#include <algorithm>
using namespace std;
#define inf 0x7fffffff
#define M 210000
#define N 220
int ans=0,num=1,n,m,h[N],h1[5*N],a[5*N],dep[N],cur[N];
struct node1{int x,y,z,next;}mp[M];
void insert(int x,int y,int z){
    mp[++num].x=x;mp[num].y=y;mp[num].z=z;mp[num].next=h[x];h[x]=num;
    mp[++num].x=y;mp[num].y=x;mp[num].z=0;mp[num].next=h[y];h[y]=num;
}
bool bfs(){
    memset(dep,0,sizeof(dep));dep[0]=1;
    queue<int>q;q.push(0);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=h[u];i;i=mp[i].next){
            int v=mp[i].y;
            if(!dep[v] && mp[i].z){
                q.push(v);
                dep[v]=dep[u]+1;
            }
        }
    }return dep[n+1];
}
int dfs(int u,int s1){
    if(u==n+1 || !s1) return s1;
    int s2=s1;
    for(int &i=cur[u];i;i=mp[i].next){
        int v=mp[i].y;
        if(dep[v]==dep[u]+1 && mp[i].z){
            int x=dfs(v,min(s1,mp[i].z));
            if(!x) dep[v]=0;
            s1-=x;mp[i].z-=x;mp[i^1].z+=x;
            if(!s1) return s2;
        }
    }return s2-s1;
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        int k;scanf("%d",&k);
        for(int j=1;j<=k;j++){
            int x;scanf("%d",&x);
            if(h1[x]) insert(h1[x],i,inf);
            else insert(0,i,a[x]);
            h1[x]=i;
        }scanf("%d",&k);insert(i,n+1,k);
    }while(bfs()){
        memcpy(cur,h,sizeof(cur));
        ans+=dfs(0,inf);
    }printf("%d\n",ans);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值