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
Problem setter: Adrian Kuegel
题目大意:
Bob和他的朋友从糖果包装里收集贴纸。这些朋友每人手里都有一些贴纸(可能重复),并且只跟别人交换他所没有的贴纸。贴纸总是一对一交换。
Bob比这些朋友聪明,因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。
假设Bob的朋友只和Bob交换,并且这些朋友只会出让手里重复贴纸来交换他们没有的不同贴纸。问Bob最终能得到的不同贴纸的最大数量。
题目分析:设有n个人,m种贴纸。对Bob的朋友(2 ~ n),朋友 i 如果第 j 种贴纸的数量超过 1 则建边(i + m,j,num[ i ][ j ] - 1),表示第 i 个朋友可以交换出至多第 j 种贴纸num[ i ][ j ] - 1张(自己还要留一张)。如果朋友 i 没有第 j 张贴纸,则建边(j, i + m, 1),表示可以和朋友 i 交换一张第 j 种贴纸(容量为 1 是因为只交换自己没有的,在交换了第一张以后,朋友 i 已经拥有该种贴纸,所以不会再要这种贴纸了)。接下来,建立超级源汇,对所有Bob有的贴纸,建边(s,i,num[ 1 ][ i ])表示Bob有第 i 种贴纸num[ 1 ][ i ]张。对所有贴纸,建边(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; }