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\n", kase++, isap());
    }
    return 0;
}

 

posted @ 2013-09-21 03:08  Ramanujan  阅读(339)  评论(0编辑  收藏  举报