B - Dining[dinic+拆点]

B - Dining

题意大概是:一个大厨,有N头牛,每头牛都会有自己喜欢吃的饭菜和喜欢喝的饮料。现在大厨做了F份饭餐,D份饮料,只有同时有吃有喝的牛才会开心。然后问,最多有几头牛可以开心。

这道题的难度在于建图,还有想到拆点这一点。
先说怎么建图吧。
我们把牛放在中间,左边食物,右边饮品,进行连线。然后源点和汇点怎么找呢? 我们让源点与所有的是食物有一条边,让汇点与所有的饮品有一条边。这样子找最大流的时候,就能保证是 源点—— 食物 —— 牛 —— 牛 —— 饮品 —— 汇点。而所有的边的权值都是1 ,这样我们找到的最大流就一定是一只牛配上一种食物和一个饮品。
而至于为要拆点,其实也很显而易见。如果不拆点,有可能两队食物和饮品同时连在同一头牛身上,这就不符合题意了。

然后难点就是读图,算法的板子就是那个样子。 我们把图读好就没有问题了。。

typedef struct Edge{
    int u, v, w, next;
}edge;

edge e[len];
int head[len];
int cur[len];
int depth[len];
int S, T, N, F, D, hcnt;
int main(){
    int Fi, Di, a, b;
    while (cin >> N >> F >> D){
        Init();
        for(int i=1; i<=F; i++) adde(0, i, 1);//源点到食物
        for(int i=1; i<=N; i++){
            cin >> Fi >> Di;
            for(int j=1; j<=Fi; j++){
                cin >> a;
                adde(a, F+i, 1);//食物 到 牛
            }
            for(int j=1; j<=Di; j++){
                cin >> b;
                adde(F+N+i, F+2*N+b, 1);//牛到饮品
            }
        }
        for (int i=1; i<=N; i++) adde(F+i, F+N+i, 1); //牛牛
        for (int i=1; i<=D; i++) adde(F+2*N+i, F+2*N+D+1, 1); //饮品到汇点
        S = 0; T = F + 2*N + D + 1;

        int ans = dinic();
        cout << ans << endl;
    }
    return 0;
}
void Init(){
    memset(head, -1, sizeof (head));
    S = T = hcnt = 0;
}

void adde(int u, int v, int w){
    e[hcnt].u = u; e[hcnt].v = v; e[hcnt].w = w;
    e[hcnt].next = head[u]; head[u] = hcnt++;
    e[hcnt].u = v; e[hcnt].v = u; e[hcnt].w = 0;
    e[hcnt].next = head[v]; head[v] = hcnt++;
}

bool BFS(){
    queue <int> q;
    for (int i=0; i<=T; i++) depth[i] = -1;
    q.push(S); depth[S] = 0;
    while (!q.empty()){
        int h = q.front(); q.pop();
        if (h == T) return true;

        for (int i=head[h]; ~i; i = e[i].next){
            int v = e[i].v;
            if (depth[v] != -1 || e[i].w <= 0)  continue;
            depth[v] = depth[h] + 1;
            q.push(v);
        }
    }
    return false;
}

int DFS(int s, int mw){
    if (s == T || mw == 0) return mw;

    int flow = 0;
    for (int i=cur[s]; ~i; i=e[i].next){
        cur[s] = i;
        int v = e[i].v;
        if (depth[v] == depth[s] + 1 && e[i].w >0){
            flow = DFS(v, min(mw, e[i].w));
            if (flow > 0){
                e[i].w -= flow;
                e[i^1].w += flow;
                return flow;
            }
        }
    }
    return 0;
}

int dinic(){
    int ans = 0;
    while(BFS()){
        for (int i=0; i<2*N+F+D+2; i++) cur[i] = head[i];
        int v = DFS(S, INF);
        ans += v;
    }
    return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值