Collector'sProblem(考虑建边的问题!最大流)

版权声明:本文为博主原创文章,转载请说明出处。 https://blog.csdn.net/xianpingping/article/details/79237213

这道题还是在考验个人对最大流的理解。最大流的考法目前来看包括最优,最大,重点就是会建边,会自己建模型。下面这道题就是运用了这种思路

这道题的重点在于建边,每一条都不能拉下。

Collector'sProblem

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


Some candymanufacturers put stickers into candy bar packages. Bob and his friendsare collecting these stickers. They all want as many different stickersas possible, but when they buy a candy bar, they don't know whichsticker is inside.
It happens that one person has duplicates of a certain sticker.Everyone trades duplicates for stickers he doesn't possess. Since allstickers 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 forhim to trade one of his duplicate stickers for a sticker he alreadypossesses.
Now assume, Bob's friends will only exchange stickers with Bob, andthey will give away only duplicate stickers in exchange with differentstickers they don't possess.
Can you help Bob and tell him the maximum number of different stickershe can get by trading stickers with his friends?

 

Input

 

The firstline 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 theselines describes Bob's stickers.
The i-th of these lines starts with a number ki<=50 indicating howmany stickers person i has.
Then follows ki numbers between 1 and m indicating which stickersperson i possesses.


Output

 

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

 

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

SampleOutput

Case #1: 1
Case #2: 3

 

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

题目大意:

Bob和他的朋友从糖果包装里收集贴纸。这些朋友每人手里都有一些贴纸(可能重复),并且只跟别人交换他所没有的贴纸。贴纸总是一对一交换。

Bob比这些朋友聪明,因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。

假设Bob的朋友只和Bob交换,并且这些朋友只会出让手里重复贴纸来交换他们没有的不同贴纸。问Bob最终能得到的不同贴纸的最大数量。



题目分析:设有n个人,m种贴纸。对Bob的朋友(2 ~ n),

我们让点1~m表示贴纸。

让点m+i表示第i个人。

num[i][j]表示第i个人拥有j种类贴纸的个数。

因为建边建的都是流量,所以建的时候就是建呗,每种情况要考虑。

1.朋友 i 如果第 j 种贴纸的数量超过 1 则建边(i + m,j,num[ i ][ j ] - 1),表示第 i 个朋友可以交换出至多第 j 种贴纸num[ i ][ j ] - 1张(自己还要留一张)。

2.如果朋友 i 没有第 j 张贴纸,则建边(j, i + m, 1),表示可以和朋友 i 交换一张第 j 种贴纸(容量为 1 是因为只交换自己没有的,在交换了第一张以后,朋友 i 已经拥有该种贴纸,所以不会再要这种贴纸了)。

3.接下来,建立超级源汇,对所有Bob有的贴纸,建边(s,i,num[ 1 ][ i ])表示Bob有第 i 种贴纸num[ 1 ][ i ]张。

4.对所有贴纸,建边(i,t,1)(每种贴纸一张就够,因为只计算种类)。接下来只要跑一遍最大流,则流量即最终结果。

代码:

#include <stdio.h>  
#include <string.h>  
#define clear(A, X) memset(A, X, sizeof A)  
#define copy(A, B) memcpy(A, B, sizeof A)  
const int maxE = 1000000;  
const int maxN = 40;  
const int maxQ = 1000000;  
const int oo = 0x3f3f3f3f;  
struct Edge{  
    int v, n, c;  
};  
Edge edge[maxE];  
int adj[maxN], cntE;  
int Q[maxQ], head, tail;  
int d[maxN], cur[maxN], pre[maxN], num[maxN];  
int s, t, nv, n, m;  
int a[maxN][maxN];  
void addedge(int u, int v, int c){  
    edge[cntE].v = v; edge[cntE].c = c; edge[cntE].n = adj[u]; adj[u] = cntE++;  
    edge[cntE].v = u; edge[cntE].c = 0; edge[cntE].n = adj[v]; adj[v] = cntE++;  
}  
void rev_bfs(){  
    clear(num, 0);  
    clear(d, -1);  
    d[t] = 0;  
    num[0] = 1;  
    head = tail = 0;  
    Q[tail++] = t;  
    while(head != tail){  
        int u = Q[head++];  
        for(int i = adj[u]; ~i; i = edge[i].n){  
            int v = edge[i].v;  
            if(~d[v]) continue;  
            d[v] = d[u] + 1;  
            Q[tail++] = v;  
            num[d[v]]++;  
        }  
    }  
}  
int ISAP(){  
    copy(cur, adj);  
    rev_bfs();  
    int flow = 0, u = pre[s] = s, i;  
    while(d[s] < nv){  
        if(u == t){  
            int f = oo, neck;  
            for(i = s; i != t; i = edge[cur[i]].v){  
                if(f > edge[cur[i]].c){  
                    f = edge[cur[i]].c;  
                    neck = i;  
                }  
            }  
            for(i = s; i != t; i = edge[cur[i]].v){  
                edge[cur[i]].c -= f;  
                edge[cur[i] ^ 1].c += f;  
            }  
            flow += f;  
            u = neck;  
        }  
        for(i = cur[u]; ~i; i = edge[i].n) if(d[edge[i].v] + 1 == d[u] && edge[i].c) break;  
        if(~i){  
            cur[u] = i;  
            pre[edge[i].v] = u;  
            u = edge[i].v;  
        }  
        else{  
            if(0 == (--num[d[u]])) break;  
            int mind = nv;  
            for(i = adj[u]; ~i; i = edge[i].n){  
                if(edge[i].c && mind > d[edge[i].v]){  
                    cur[u] = i;  
                    mind = d[edge[i].v];  
                }  
            }  
            d[u] = mind + 1;  
            num[d[u]]++;  
            u = pre[u];  
        }  
    }  
    return flow;  
}  
void init(){  
    clear(adj, -1);  
    cntE = 0;  
}  
void work(){  
    int h, p;  
    init();  
    clear(a, 0);  
    scanf("%d%d", &n, &m);  
    s = 0; t = n + m + 1; nv = t + 1;  
    for(int i = 1; i <= n; ++i){  
        scanf("%d", &h);  
        for(int j = 0; j < h; ++j){  
            scanf("%d", &p);  
            ++a[i][p];  
        }  
    }  
    for(int i = 1; i <= m; ++i){  
        if(a[1][i]) 
addedge(s, i, a[1][i]);  
        addedge(i, t, 1);  
    }  
    for(int i = 2; i <= n; ++i) for(int j = 1; j <= m; ++j){  
        if(a[i][j] - 1 > 0) addedge(i + m, j, a[i][j] - 1);  
        else if(!a[i][j]) addedge(j, i + m, 1);  
    }  
    printf("%d\n", ISAP());  
}  
int main(){  
    int T, cas = 1;  
    for(scanf("%d", &T), cas = 1; cas <= T; ++cas){  
        printf("Case #%d: ", cas);        
        work();  
    }  
    return 0;  
}  



展开阅读全文

没有更多推荐了,返回首页