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;
    }
    
  • 相关阅读:
    何必言精通——十年杂感 兼谈其它 总结
    寻找自己的道路——与技术同胞共勉 一种划分为七个阶段的道路:自信=>意志=>布局=>切入点=>团队=>渠道=>产品
    务虚:大局观、方法与关键点 个人经历例子说明 一种工作应对解决的方法
    程序员“宅钱”的几种方式和我的体会 程序员其他赚钱的路径:私单、站长、开发共享软件
    怎么看待移动互联网时代 关于移动互联网时代的一点个人看法总结 在强烈的产业变化时期,主流观点是不靠谱的 什么是浪潮呢? 小型化、无线化、智能化。
    为什么现在很多年轻人愿意来北上广深打拼,即使过得异常艰苦,远离亲人,仍然义无反顾? 谈谈程序员返回家乡的创业问题 利基市场就是那些不大不小的缝隙中的市场 马斯洛的需求无层次不适合中国。国人的需求分三个层次——生存、稳定、装逼。对应的,国内的产品也分三个层次——便宜、好用、装B。人们愿意为这些掏钱
    SSM项目的数据库密码加密方案
    Python两个变量的值进行交换的方法
    Python编码问题
    Python2.7.14新手学习
  • 原文地址:https://www.cnblogs.com/ramanujan/p/3330959.html
Copyright © 2011-2022 走看看