UVA10779 Collectors Problem
题目链接: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\n",kase++,sap(s,t,2+m+n)); 135 } 136 return 0; 137 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”