题目链接:https://cn.vjudge.net/problem/UVA-10779
前言:
本题是关于姜志豪《网络流的一些建模方法》的笔记。
知识点: 最大流
题意摘抄:
(Bob) 和他的朋友从糖果包装里收集贴纸。(Bob) 和他的朋友总共 (n) 人。共有 (m) 种不同的贴纸。
每人手里都有一些(可能有重复的)贴纸,并且只跟别人交换他所没有的贴纸。贴纸总是一对一交换。
(Bob) 比这些朋友更聪明,因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。在某些情况下,换来一张重复贴纸更划算。
假设 (Bob) 的朋友只和 (Bob) 交换(他们之间不交换),并且这些朋友只会出让手里的重复贴纸来交换他们没有的不同贴纸。你的任务是帮助 (Bob) 算出他最终可以得到的不同贴纸的最大数量。
((2 le n le 10, 5 le m le 25))
解题思路:
每种贴纸、每个朋友都各视为一个点,再加上一个源点和一个汇点。
对于每种贴纸,建一条从源点到这种贴纸的边,容量为 (Bob) 所拥有的这种贴纸的张数;
对于每一个朋友,如果他没有某种贴纸,就建一条从这种贴纸到他的边,容量为 (1);如果他有这种贴纸并且拥有量 (k) 大于(1) ,则建一条从他到这种贴纸的边,容量为 (k-1).
对于每种贴纸,再建一条边到汇点,容量为 (1).
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 510;//点数的最大值 4 const int MAXM = 15000;//边数的最大值 5 const int INF = 0x3f3f3f3f; 6 7 struct Edge{ 8 int to,next,cap,flow; 9 }edge[MAXM];//注意是MAXM 10 int tol; 11 int head[MAXN]; 12 int gap[MAXN],dep[MAXN],cur[MAXN]; 13 void init(){ 14 tol = 0; 15 memset(head,-1,sizeof(head)); 16 } 17 void addedge(int u,int v,int w,int rw = 0){ 18 edge[tol].to = v; edge[tol].cap = w; edge[tol].flow = 0; 19 edge[tol].next = head[u]; head[u] = tol++; 20 edge[tol].to = u; edge[tol].cap = rw; edge[tol].flow = 0; 21 edge[tol].next = head[v]; head[v] = tol++; 22 } 23 int Q[MAXN]; 24 void BFS(int start,int end){ 25 memset(dep,-1,sizeof(dep)); 26 memset(gap,0,sizeof(gap)); 27 gap[0] = 1; 28 int front = 0, rear = 0; 29 dep[end] = 0; 30 Q[rear++] = end; 31 while(front != rear){ 32 int u = Q[front++]; 33 for(int i = head[u]; i != -1; i = edge[i].next){ 34 int v = edge[i].to; 35 if(dep[v] != -1) 36 continue; 37 Q[rear++] = v; 38 dep[v] = dep[u] + 1; 39 gap[dep[v]]++; 40 } 41 } 42 } 43 int S[MAXN]; 44 int sap(int start,int end,int N){ 45 BFS(start,end); 46 memcpy(cur,head,sizeof(head)); 47 int top = 0; 48 int u = start; 49 int ans = 0; 50 while(dep[start] < N){ 51 if(u == end){ 52 int Min = INF; 53 int inser; 54 for(int i = 0;i < top;i++) 55 if(Min > edge[S[i]].cap - edge[S[i]].flow){ 56 Min = edge[S[i]].cap - edge[S[i]].flow; 57 inser = i; 58 } 59 for(int i = 0;i < top;i++){ 60 edge[S[i]].flow += Min; 61 edge[S[i]^1].flow -= Min; 62 } 63 ans += Min; 64 top = inser; 65 u = edge[S[top]^1].to; 66 continue; 67 } 68 bool flag = false; 69 int v; 70 for(int i = cur[u]; i != -1; i = edge[i].next){ 71 v = edge[i].to; 72 if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]){ 73 flag = true; 74 cur[u] = i; 75 break; 76 } 77 } 78 if(flag){ 79 S[top++] = cur[u]; 80 u = v; 81 continue; 82 } 83 int Min = N; 84 for(int i = head[u]; i != -1; i = edge[i].next) 85 if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min){ 86 Min = dep[edge[i].to]; 87 cur[u] = i; 88 } 89 gap[dep[u]]--; 90 if(!gap[dep[u]]) 91 return ans; 92 dep[u] = Min + 1; 93 gap[dep[u]]++; 94 if(u != start) 95 u = edge[S[--top]^1].to; 96 } 97 return ans; 98 } 99 int has[30]; 100 int main(){ 101 int T; 102 int n,m,k; 103 int num; 104 int kase=1; 105 scanf("%d",&T); 106 while(T--){ 107 init(); 108 int s=0,t=MAXN-1; 109 memset(has,0,sizeof(has)); 110 scanf("%d%d",&n,&m); 111 scanf("%d",&k); 112 while(k--){ 113 scanf("%d",&num); 114 has[num]++; 115 } 116 for(int i=1;i<=m;i++){ 117 addedge(s,i,has[i]); 118 addedge(i,t,1); 119 } 120 for(int i=1;i<n;i++){ 121 memset(has,0,sizeof(has)); 122 scanf("%d",&k); 123 while(k--){ 124 scanf("%d",&num); 125 has[num]++; 126 } 127 for(int j=1;j<=m;j++){ 128 if(!has[j]) 129 addedge(j,30+i,1); 130 else if(has[j]>1) 131 addedge(30+i,j,has[j]-1); 132 } 133 } 134 printf("Case #%d: %d ",kase++,sap(s,t,2+m+n)); 135 } 136 return 0; 137 }