UVa 10779 Collectors Problem 最大流

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.

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值