zoukankan      html  css  js  c++  java
  • POJ 3281_Dining

    题意:

    FJ准备了F种食物和D种饮料,每头牛都有喜欢的食物和饮料,并且每头牛都只能分配一种食物和饮料。问如何分配使得同时得到喜欢的食物和饮料的牛数量最多。

    分析:

    首先想到将牛与其对应的食物和饮料匹配起来,即在食物、饮料与牛之间连一条边,再在s和所有食物之间、t和所有饮料之间连一条边。这样每一条路径都对应着食物饮料和牛之间的匹配方案。那么如何避免一头牛被分配多组匹配呢?就将一头牛拆成两个结点,并用一条容量为1的边连接起来,这样求出构成的图中的最大流,即得解。这里使用的是Dinic算法。

    代码:

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<queue>
    using namespace std;
    struct edge{int to, cap, rev;};
    const int maxn = 105, maxm = 2000055, INF = 0x3fffffff;
    int d[maxm], iter[maxm];
    int s, t;
    vector<edge>G[maxm];
    int dr[maxn][maxn], f[maxn][maxn];
    void add_edge(int from, int to, int cap)
    {
        G[from].push_back((edge){to, cap, G[to].size()});
        G[to].push_back((edge){from, 0, G[from].size()-1});
    }
    void bfs()
    {
        memset(d, -1, sizeof(d));
        queue<int>q;
        d[s] = 0;
        q.push(s);
        while(!q.empty()){
            int v = q.front();q.pop();
            for(int i = 0; i <G[v].size(); i++){
                edge &e = G[v][i];
                if(e.cap>0&&d[e.to]<0){
                    d[e.to] = d[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }
    int dfs(int v, int f)
    {
        if(v==t) return f;
        for(int &i = iter[v]; i < G[v].size(); i++){
            edge &e = G[v][i];
            if(e.cap > 0 && d[v] < d[e.to]){
                int tf = dfs(e.to, min(f, e.cap));
                if(tf > 0){
                    e.cap -= tf;
                    G[e.to][e.rev].cap +=tf;
                    return tf;
                }
            }
        }
        return 0;
    }
    int max_flow()
    {
        int flow = 0;
        for(;;){
            bfs();
            if(d[t]<0) return flow;
            memset(iter, 0, sizeof(iter));
            int f;
            while((f = dfs(s, INF))>0){
                flow += f;
            }
        }
    }
    int main (void)
    {
        int N, F, D;scanf("%d%d%d",&N, &F, &D);
        int a, b;
        s = 2 * N + F + D + 1, t = s + 1;
        for(int i = 1; i <= N; i++){
            scanf("%d%d",&a, &b);
            add_edge(i, N + i, 1);
            for(int j = 0; j < a; j++){
                scanf("%d",&f[i][j]);
                add_edge(2 * N + f[i][j], i, 1);
            }
            for(int j = 0; j < b; j++){
                scanf("%d",&dr[i][j]);
                add_edge(N + i, 2 * N + F + dr[i][j], 1);
            }
        }
        for(int i = 1; i <= F; i++)
             add_edge(s, 2 * N + i, 1);
        for(int i = 1; i <= D; i++)
            add_edge(2 * N + F + i, t, 1);
    
        printf("%d
    ",max_flow());
    }
    

    增广路径必须满足的性质
    1.有奇数条边。
    2.起点在二分图的左半边,终点在右半边。
    3.路径上的点一定是一个在左半边,一个在右半边,交替出现。(其实二分图的性质就决定了这一点,因为二分图同一边的点之间没有边相连,不要忘记哦。)
    4.整条路径上没有重复的点。
    5.起点和终点都是目前还没有配对的点,而其它所有点都是已经配好对的。
    6.路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
    7.最后,也是最重要的一条,把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原匹配中删除(这个操作称为增广路径的取反),则新的匹配数就比原匹配数增加了1个(奇数=偶数+1)。

  • 相关阅读:
    UVA
    UVA
    模板——扩展欧几里得算法(求ax+by=gcd的解)
    UVA
    模板——2.2 素数筛选和合数分解
    模板——素数筛选
    Educational Codeforces Round 46 (Rated for Div. 2)
    Educational Codeforces Round 46 (Rated for Div. 2) E. We Need More Bosses
    Educational Codeforces Round 46 (Rated for Div. 2) D. Yet Another Problem On a Subsequence
    Educational Codeforces Round 46 (Rated for Div. 2) C. Covered Points Count
  • 原文地址:https://www.cnblogs.com/Tuesdayzz/p/5758773.html
Copyright © 2011-2022 走看看