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