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

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比这些朋友聪明，因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。

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;
}