zoukankan      html  css  js  c++  java
  • uva 10779 ISAP 最大流

    Collector's Problem

    Input: standard input
    Output: standard output
    Time Limit: 5 seconds


    Some candy manufacturers put stickers into candy bar packages. Bob and his friends are collecting these stickers. They all want as many different stickers as possible, but when they buy a candy bar, they don't know which sticker is inside.
    It happens that one person has duplicates of a certain sticker. Everyone trades duplicates for stickers he doesn't possess. Since all stickers have the same value, the exchange ratio is always 1:1.
    But Bob is clever: he has realized that in some cases it is good for him to trade one of his duplicate stickers for a sticker he already possesses.
    Now assume, Bob's friends will only exchange stickers with Bob, and they will give away only duplicate stickers in exchange with different stickers they don't possess.
    Can you help Bob and tell him the maximum number of different stickers he can get by trading stickers with his friends?

     

    Input

     

    The first line of input contains the number of cases T (T<=20).
    The first line of each case contains two integers n and m (2<=n<=10, 5<=m<=25). n is the number of people involved (including Bob), and m is the number of different stickers available.
    The next n lines describe each person's stickers; the first of these lines describes Bob's stickers.
    The i-th of these lines starts with a number ki<=50 indicating how many stickers person i has.
    Then follows ki numbers between 1 and m indicating which stickers person i possesses.


    Output

    For each case, print the test case number together with the maximum number of different stickers Bob can get.

     

    Sample Input

    2
    2 5
    6 1 1 1 1 1 1
    3 1 2 2
    3 5
    4 1 2 1 1
    3 2 2 2
    5 1 3 4 4 3

    Sample Output

    Case #1: 1
    Case #2: 3

     

    Explanation of the sample output:
    In the first case, no exchange is possible, therefore Bob can have only the sticker with number 1.
    In the second case, Bob can exchange a sticker with number 1 against a sticker with number 2 of the second person,
    and then this sticker against a sticker with number 3 or 4 of the third person, and now he has stickers 1, 2 and 3 or 1, 2 and 4.

    Problem setter: Adrian Kuegel

    最大流算法,建模:

    1.以Bob为源点向每个sticker引一条容量为对应sticker数量(!0)的弧;

    2.然后对每对(stikcer-u, people-v)若v有u的数量多于1,则说明v可以拿number[u]-1个数来交换,从v向u连一条容量为number[u]-1的弧;否则若number[u]=0,则说明v可以交换u进来1个u,所以从u连一条指向v的容量为1的弧;

    3.最后,因为要求的是Bob最多可获得多少种sticker,所以从每个sticker连一条指向源点的容量为1的弧。 

    答案即为最大流。

    为什么这样可以保证除Bob以外别的人不会直接交换呢?

    最大流是从s,即Bob出发的,最大流肯定不大于Bob的sticker总数,再根据增广过程即可知答案是正确的。详细注释的 AC代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<vector>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    
    using namespace std;
    
    #define LL long long
    #define ULL unsigned long long
    #define UINT unsigned int
    #define MAX_INT 0x7fffffff
    #define MAX_LL 0x7fffffffffffffff
    #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
    #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
    
    #define MAXN 36
    #define MAXM 1000
    #define INF 100000000
    
    struct edge{
        int u, v, cap, flow, nxt;
    }e[MAXM];
    int h[MAXN], cc, n;
    
    void add(int u, int v, int cap){
        e[cc]=(edge){u, v, cap, 0, h[u]};
        h[u]=cc++;
        e[cc]=(edge){v, u, 0, 0, h[v]};
        h[v]=cc++;
    }
    
    int s, t;
    int d[MAXN], p[MAXN], num[MAXN];
    
    int vis[MAXN];
    void bfs(){
        int q[MAXN], *head, *rear;  head=rear=q;
        memset(vis, 0, sizeof(vis));
        d[t]=0;     vis[t]=1;
        *(rear++)=t;
        while(head<rear){
            int u=*(head++);
            for(int i=h[u]; i!=-1; i=e[i].nxt){
                i^=1;
                int v=e[i].u, cap=e[i].cap, flow=e[i].flow;
                if(!vis[v] && cap>flow){
                    vis[v]=1;
                    d[v]=d[u]+1;
                    *(rear++)=v;
                }
                i^=1;
            }
        }
    }
    
    int Augment(){
        int u=t, a=INF;
        while(u!=s){
            a=MIN(a, e[p[u]].cap-e[p[u]].flow);
            u=e[p[u]].u;
        }
        for(u=t; u!=s; u=e[p[u]].u){
            e[p[u]].flow+=a;
            e[p[u]^1].flow-=a;
        }
        return a;
    }
    
    int cur[MAXN];
    int isap(){
        int flow=0, i;
        bfs();                              //reverse bfs
        memset(num, 0, sizeof(num));
        for(i=0; i<n; i++) num[d[i]]++;
        for(i=0; i<n; i++) cur[i]=h[i];
        int u=s;
        while(d[s]<n){              
            if(u==t){
                flow+=Augment();            //reverse updating
                u=s;
            }
            int ok=0;
            for(i=cur[u]; i!=-1; i=e[i].nxt){                   //advance
                int v=e[i].v, ef=e[i].flow, cap=e[i].cap;
                if(d[v]+1==d[u] && cap>ef){
                    ok=1;
                    p[v]=i;
                    cur[u]=i;                       //优化
                    u=v;
                    break;
                }
            }
            if(!ok){                                            //retreat
                int tmp=n-1;
                for(i=h[u]; i!=-1; i=e[i].nxt) if(e[i].cap>e[i].flow)
                    tmp=MIN(tmp, d[e[i].v]);                //想象下用Dinic bfs构造层次图的条件
                if(--num[d[u]]==0) break;                   //gap优化
                num[d[u]=tmp+1]++;
                cur[u]=h[u];                        //因为更新了层次图,所以需要从第一条边开始枚举
                if(u!=s) u=e[p[u]].u;
            }
        }
        return flow;
    }
    
    int main(){
    //    freopen("C:\Users\Administrator\Desktop\in.txt","r",stdin);
        int nn, T, mm, kase=1;
        scanf(" %d", &T);
        while(T--){
            scanf(" %d %d", &nn, &mm);        s=0;  t=nn+mm;  n=t+1;
            int i, j, num[MAXN];
            memset(h, -1, sizeof(h));       cc=0;
            for(i=0; i<nn; i++){
                int k, tmp;
                scanf(" %d", &k);
                memset(num, 0, sizeof(num));
                for(j=0; j<k; j++) scanf(" %d", &tmp), num[tmp]++;          //这里开始用t代替tmp导致各种错。。。
                if(i==0){
                    for(j=1; j<=mm; j++) if(num[j]) 
                        add(s, j, num[j]);                          //Bob可以拿来交换的sticker数
                    continue;
                }
                for(j=1; j<=mm; j++){
                    if(num[j]>1) add(i+mm, j, num[j]-1);            //每个人可以拿来交换的sticker数
                    else if(!num[j]) add(j, i+mm, 1);               //每个人可以交换进来的sticker数
                }
            }
            for(i=1; i<=mm; i++) add(i, t, 1);                      //Bob可能获得的sticker种类
            printf("Case #%d: %d
    ", kase++, isap());
        }
        return 0;
    }
    
  • 相关阅读:
    CSS3弹性盒布局方式
    Vue知识点(面试常见点)
    h5新增加的存储方法
    前端常用插件
    Git 及 GitHub 使用
    Express 框架
    angular.js 教程 -- 实例讲解
    Windows右键添加VSCode启动
    Windows平台SSH登录Linux并使用图形化界面
    10_Linux yum命令
  • 原文地址:https://www.cnblogs.com/ramanujan/p/3330959.html
Copyright © 2011-2022 走看看