POJ 2987:Firing(最大权闭合图)
http://poj.org/problem?id=2987
题意:有公司要裁员,每裁一个人可以得到收益(有正有负),而且如果裁掉的这个人有党羽的话,必须将这个人的所有党羽都裁除,问最少的裁员人数是多少和最大收益是多少。
思路:有依赖关系,最大权闭合图。我们要得到最大收益,那么就是尽量选择更多收益为正数的人,选择更少收益为负数的人,因此我们将收益为正数的人与源点连一条边,将收益为负数的人与汇点连一条边,这样得到的割集就是未选择的收益为正数的人+选择的收益为负数的人(也可以是损失的收益),很明显答案要使这个割集越小越好,那么就是求最小割。我们能够得到的最大收益是所有正数的和sum,我们再用sum-最小割(损失的收益)就可以得到最大收益了。
至于最少的裁员人数,我也挺迷糊的。看到别人的“将从S出发能到达的点看做firing的集合中的点,其他的能到T的点看作不被firing的集合中的点,那么在做完最大流之后自然就不会有firing集合中的点指向不被firing的集合中的点的边了,否则就会形成增广路。”这句话后,似乎我们只要在最终的残余网络中,只要能从S遍历到的点,就可以看成是被裁掉的点,因为最终肯定会遇到割边达不到T,所以我们直接DFS残余网络的点数就可以得到最少的裁员人数了。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <algorithm> 5 using namespace std; 6 #define N 5010 7 #define INF 0x3f3f3f3f 8 typedef long long LL; 9 struct Edge { 10 int v, nxt, cap; 11 } edge[100000]; 12 int head[N], cur[N], dis[N], pre[N], gap[N], vis[N], S, T, tot; 13 14 void Add(int u, int v, int cap) { 15 edge[tot] = (Edge) {v, head[u], cap}; head[u] = tot++; 16 edge[tot] = (Edge) {u, head[v], 0}; head[v] = tot++; 17 } 18 19 int BFS() { 20 memset(dis, INF, sizeof(dis)); 21 memset(gap, 0, sizeof(gap)); 22 queue<int> que; que.push(T); 23 dis[T] = 0; gap[0]++; 24 while(!que.empty()) { 25 int u = que.front(); que.pop(); 26 for(int i = head[u]; ~i; i = edge[i].nxt) { 27 int v = edge[i].v; 28 if(dis[v] < INF) continue; 29 dis[v] = dis[u] + 1; 30 gap[dis[v]]++; 31 que.push(v); 32 } 33 } 34 } 35 36 LL ISAP(int n) { 37 BFS(); 38 memcpy(cur, head, sizeof(cur)); 39 int u = pre[S] = S, i, flow, index; LL ans = 0; 40 while(dis[S] < n) { 41 if(u == T) { 42 flow = INF; 43 for(i = S; i != T; i = edge[cur[i]].v) 44 if(flow > edge[cur[i]].cap) flow = edge[cur[i]].cap, index = i; 45 for(i = S; i != T; i = edge[cur[i]].v) 46 edge[cur[i]].cap -= flow, edge[cur[i]^1].cap += flow; 47 u = index; ans += flow; 48 } 49 for(i = cur[u]; ~i; i = edge[i].nxt) if(edge[i].cap > 0 && dis[edge[i].v] + 1 == dis[u]) break; 50 if(~i) { 51 cur[u] = i; pre[edge[i].v] = u; u = edge[i].v; 52 } else { 53 int md = n + 1; 54 if(--gap[dis[u]] == 0) break; 55 for(i = head[u]; ~i; i = edge[i].nxt) 56 if(edge[i].cap > 0 && dis[edge[i].v] < md) md = dis[edge[i].v], cur[u] = i; 57 gap[dis[u] = md + 1]++; 58 u = pre[u]; 59 } 60 } 61 return ans; 62 } 63 64 int DFS(int u) { 65 vis[u] = 1; int ans = 1; 66 for(int i = head[u]; ~i; i = edge[i].nxt) 67 if(edge[i].cap > 0 && !vis[edge[i].v]) ans += DFS(edge[i].v); 68 return ans; 69 } 70 71 int main() { 72 int n, m; 73 while(~scanf("%d%d", &n, &m)) { 74 S = 0, T = n + 1; 75 LL sum = 0; int u, v; tot = 0; 76 memset(head, -1, sizeof(head)); 77 memset(vis, 0, sizeof(vis)); 78 for(int i = 1; i <= n; i++) { 79 int w; scanf("%d", &w); 80 if(w > 0) Add(S, i, w), sum += w; 81 else Add(i, T, -w); 82 } 83 for(int i = 1; i <= m; i++) { 84 int u, v; 85 scanf("%d%d", &u, &v); 86 Add(u, v, INF); 87 } 88 LL ans = ISAP(T + 1); 89 int res = DFS(S); 90 printf("%d %lld\n", --res, sum - ans); 91 } 92 return 0; 93 }