题目1 POJ2987
题目大意:
一个公司要裁员,每个成员都有自己的效益值,可正可负,而且每个人都有自己的直接下属,如果某个人被裁员,那么他的直接下属,他的下属的下属。。。。都会离开这家公司。
现在请你确定裁员的方案,求最小裁员人数和公司的最大收益。
算法讨论:
选了一个点,其后继都必须要选,这是闭合子图的特点。所以这个题就是裸题啦。
最小裁员人数就是与S相连的点数,公司的最大收益就是正权和-最小割。
Code:
1 #include <cstdlib> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <iostream> 6 #include <queue> 7 #include <vector> 8 9 using namespace std; 10 typedef long long ll; 11 const int N = 50000 + 5; 12 const ll oo = 10000000000000000LL; 13 14 struct Edge { 15 int from, to; 16 ll cap, flow; 17 Edge(int u = 0, int v = 0, ll cp = 0, ll fw = 0): 18 from(u), to(v), cap(cp), flow(fw) {} 19 }; 20 21 struct Dinic { 22 int n, m, s, t; 23 int cur[N], que[N * 10]; 24 ll dis[N]; 25 bool vis[N]; 26 vector <Edge> edges; 27 vector <int> G[N]; 28 29 void add(int from, int to, ll cp) { 30 edges.push_back(Edge(from, to, cp, 0)); 31 edges.push_back(Edge(to, from, 0, 0)); 32 m = edges.size(); 33 G[from].push_back(m - 2); 34 G[to].push_back(m - 1); 35 } 36 37 bool bfs() { 38 int head = 1, tail = 1; 39 memset(vis, false, sizeof vis); 40 dis[s] = 0; vis[s] = true; que[head] = s; 41 while(head <= tail) { 42 int x = que[head]; 43 for(int i = 0; i < (signed) G[x].size(); ++ i) { 44 Edge &e = edges[G[x][i]]; 45 if(!vis[e.to] && e.cap > e.flow) { 46 vis[e.to] = true; 47 dis[e.to] = dis[x] + 1; 48 que[++ tail] = e.to; 49 } 50 } 51 ++ head; 52 } 53 return vis[t]; 54 } 55 56 ll dfs(int x, ll a) { 57 if(x == t || a == 0) return a; 58 ll flw = 0, f; 59 for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { 60 Edge &e = edges[G[x][i]]; 61 if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { 62 e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f; 63 if(a == 0) break; 64 } 65 } 66 return flw; 67 } 68 69 ll mxflow(int s, int t) { 70 this->s = s; this->t = t; 71 ll flw = 0; 72 while(bfs()) { 73 memset(cur, 0, sizeof cur); 74 flw += dfs(s, oo); 75 } 76 return flw; 77 } 78 79 int bfs(int s) { 80 queue <int> q; 81 memset(vis, false, sizeof vis); 82 int cnt = 1; 83 q.push(s); vis[s] = true; 84 while(!q.empty()) { 85 int x = q.front(); q.pop(); 86 for(int i = 0; i < (signed)G[x].size(); ++ i) { 87 Edge e = edges[G[x][i]]; 88 if(!vis[e.to] && e.cap > e.flow) { 89 vis[e.to] = true; 90 ++ cnt; 91 q.push(e.to); 92 } 93 } 94 } 95 return cnt - 1; 96 } 97 }net; 98 99 int n, m, S, T, w[N]; 100 101 int main() { 102 int ou = 0, as, bs; 103 ll tot = 0, c = 0; 104 scanf("%d%d", &n, &m); 105 S = 0; T = n + 1; 106 for(int i = 1; i <= n; ++ i) { 107 scanf("%d", &w[i]); 108 if(w[i] > 0) { 109 net.add(S, i, w[i]); 110 tot += w[i]; 111 } 112 else if(w[i] < 0) { 113 net.add(i, T, -w[i]); 114 } 115 } 116 for(int i = 1; i <= m; ++ i) { 117 scanf("%d%d", &as, &bs); 118 net.add(as, bs, oo); 119 } 120 c = net.mxflow(S, T); 121 tot = tot - c; 122 ou = net.bfs(S); 123 printf("%d %lld ", ou, tot); 124 return 0; 125 }
题目2 HDU3879
题目大意:
要选择位置建立通信站,在第i个位置建立要花费的代价 为wi,如果i .. j两个点都建立了通信站,那么这两个点之间就可以进行通信,而且可以收获c的收益。
求最大收益。
算法讨论:
我们把所有事情都看做一个事件,建立一个站是一个负权点,权值为wi,两个点都建立通信站是一个正权点,权值为c,而且这个点发生的前提是这两个点都已经建立了站点。
所以满足闭合子图的定义,就这样直接建立网络流的模型即可。
代码:
1 #include <cstdlib> 2 #include <cstdio> 3 #include <iostream> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 8 using namespace std; 9 10 const int N = 55000 + 5; 11 const int oo = 0x3f3f3f3f; 12 13 struct Edge { 14 int from, to, cap, flow; 15 Edge(int u = 0, int v = 0, int cp = 0, int fw = 0) : 16 from(u), to(v), cap(cp), flow(fw) {} 17 }; 18 19 struct Dinic { 20 int n, m, s, t; 21 int dis[N], cur[N], que[N * 10]; 22 bool vis[N]; 23 vector <Edge> edges; 24 vector <int> G[N]; 25 26 void clear() { 27 for(int i = 0; i <= n; ++ i) G[i].clear(); 28 edges.clear(); 29 n = 0; s = t = 0; 30 } 31 32 void add(int from, int to, int cap) { 33 edges.push_back(Edge(from, to, cap, 0)); 34 edges.push_back(Edge(to, from, 0, 0)); 35 m = edges.size(); 36 G[from].push_back(m - 2); 37 G[to].push_back(m - 1); 38 } 39 40 bool bfs() { 41 int head = 1, tail = 1; 42 memset(vis, false, sizeof vis); 43 dis[s] = 0; vis[s] = true; que[head] = s; 44 while(head <= tail) { 45 int x = que[head]; 46 for(int i = 0; i < (signed) G[x].size(); ++ i) { 47 Edge &e = edges[G[x][i]]; 48 if(!vis[e.to] && e.cap > e.flow) { 49 vis[e.to] = true; 50 dis[e.to] = dis[x] + 1; 51 que[++ tail] = e.to; 52 } 53 } 54 ++ head; 55 } 56 return vis[t]; 57 } 58 59 int dfs(int x, int a) { 60 if(x == t || a == 0) return a; 61 int flw = 0, f; 62 for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { 63 Edge &e = edges[G[x][i]]; 64 if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { 65 e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f; 66 if(a == 0) break; 67 } 68 } 69 return flw; 70 } 71 72 int mx(int s, int t) { 73 this->s = s; this->t = t; 74 int flw = 0; 75 while(bfs()) { 76 memset(cur, 0, sizeof cur); 77 flw += dfs(s, oo); 78 } 79 return flw; 80 } 81 }net; 82 83 int n, m, w[N]; 84 int S, T; 85 86 int main() { 87 int tot = 0, a, b, c; 88 while(~scanf("%d%d", &n, &m)) { 89 net.clear(); tot = 0; 90 net.n = n + m + 1; 91 S = 0; T = n + m + 1; 92 for(int i = 1; i <= n; ++ i) { 93 scanf("%d", &w[i]); 94 net.add(i + m, T, w[i]); 95 } 96 for(int i = 1; i <= m; ++ i) { 97 scanf("%d%d%d", &a, &b, &c); 98 net.add(S, i, c); 99 tot += c; 100 net.add(i, a + m, oo); 101 net.add(i, b + m, oo); 102 } 103 printf("%d ", tot - net.mx(S, T)); 104 } 105 return 0; 106 }