【bzoj 1287】Cards交换卡片(网络流)

传送门biu~
把每种卡片建一个节点,每位除了小C以外的小朋友建一个节点。如果小C有一种卡片的张数大于1,则连一条从源点到此种卡片的弧,流量为张数减一;如果小C有一种卡片的张数为0,则连一条此种卡片到汇点的弧,流量为1。同理,如果一个小朋友有一种卡片的张数大于1,则连一条这个小朋友到此种卡片的弧,流量为张数减一;如果一个小朋友有一种卡片的张数为0,则连一条此种卡片到这个小朋友的弧,流量为1。显然跑一边网络最大流所得的答案即为小C多获得的卡片的种类数,加上小C已有的种类数即为所求答案。

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
int n,m,ans,S,T;
int card[205][205];
int fir[505],head[505],dep[505],nex[500005],to[500005],cap[500005],tp=1;
inline void add(int x,int y,int c){
    nex[++tp]=head[x];
    head[x]=tp;
    to[tp]=y;
    cap[tp]=c;
}
inline int bfs(){
    memset(dep,0,sizeof(dep));
    dep[S]=1;
    queue<int>q;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nex[i]){
            if(!dep[to[i]] && cap[i]){
                dep[to[i]]=dep[x]+1;
                q.push(to[i]);
            }
        }
    }
    return dep[T];
}
int dfs(int x,int now){
    if(x==T || !now)    return now;
    int c=0;
    for(int &i=fir[x];i;i=nex[i]){
        if(dep[to[i]]==dep[x]+1 && cap[i]){
            int f=dfs(to[i],min(cap[i],now));
            cap[i]-=f;
            cap[i^1]+=f;
            c+=f;
            now-=f;
        }
    }
    return c;
}
inline int Dinic(){
    int c=0;
    while(bfs()){
        for(int i=1;i<=n+m;++i) fir[i]=head[i];
        fir[S]=head[S];fir[T]=head[T];
        c+=dfs(S,INF);
    }
    return c;
}
int main(){
    scanf("%d%d",&n,&m);S=n+m+1;T=n+m+2;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            scanf("%d",&card[i][j]);
            if(i==1){
                 if(card[i][j]) ++ans;
            }
            else{
                if(card[i][j]==0)   add(j,m+i,1),add(m+i,j,0);
                if(card[i][j]>1)    add(m+i,j,card[i][j]-1),add(j,m+i,0);
            }
        }
    }
    for(int i=1;i<=m;++i){
        if(card[1][i]>1)    add(S,i,card[1][i]-1),add(i,S,0);
        if(!card[1][i])     add(i,T,1),add(T,i,0);
    }
    printf("%d",ans+Dinic());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zP1nG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值