BZOJ 2039:[2009国家集训队]employ人员雇佣(最小割)
http://www.lydsy.com/JudgeOnline/problem.php?id=2039
题意:中文题意。
思路:一开始想着和之前做的最大权闭合图有点像,但是如果把边全部当成点的话,那么点也太多了。
对于这种选和不选的方案问题,还是一样用最小割来解决,求最小的损失收益,然后用能够得到的总收益去减去最小割的值就是答案。思维模式是:将S集或者T集的某一边当成是选择的,另一边当成是不选择的,根据这样去连边,就可能更容易想到建图方案。
在这题里面,我把T集当成是选择的人,把S集当成是不选择的人。这样的话如果选择一个人,那么就损失了雇佣他的钱,于是将A[i]和S相连,代表如果不割这条边的话,那么它就会划入S集。同理如果不雇佣一个人,损失的E[i]和T相连,代表如果不割这条边的话,那么它就划入T集,代表选择这个人。有了上面这两个主要的建边,接下来就很容易想到将人与人之间连一条容量为互相的贡献E[i,j]*2(题意)的无向边,代表如果选了i,那么可以对j作出E[i,j]的贡献,选j同理。
这样图的点数其实就是人数+2而已。记得要开longlong,还有损失的E[i]要先合并起来,否则边过多。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 #define N 1010 7 #define INF 0x3f3f3f3f 8 typedef long long LL; 9 struct Edge { 10 int u, v; LL cap; int nxt; 11 } edge[N*N*4]; 12 int cur[N], gap[N], dis[N], pre[N], tot, head[N], S, T; 13 LL mp[N][N], e[N]; 14 15 void Add(int u, int v, LL cap) { 16 edge[tot] = (Edge) {u, v, cap, head[u]}; head[u] = tot++; 17 edge[tot] = (Edge) {v, u, 0, head[v]}; head[v] = tot++; 18 } 19 20 int BFS() { 21 queue<int> que; 22 que.push(T); 23 memset(dis, INF, sizeof(dis)); 24 memset(gap, 0, sizeof(gap)); 25 dis[T] = 0; gap[0]++; 26 while(!que.empty()) { 27 int u = que.front(); que.pop(); 28 for(int i = head[u]; ~i; i = edge[i].nxt) { 29 if(dis[edge[i].v] == INF) { 30 dis[edge[i].v] = dis[u] + 1; 31 gap[dis[edge[i].v]]++; 32 que.push(edge[i].v); 33 } 34 } 35 } 36 } 37 38 LL ISAP(int n) { 39 BFS(); 40 memcpy(cur, head, sizeof(cur)); 41 LL ans = 0, flow; 42 int i, u = pre[S] = S, index; 43 while(dis[S] < n) { 44 if(u == T) { 45 flow = 100000000000000000; 46 for(i = S; i != T; i = edge[cur[i]].v) 47 if(flow > edge[cur[i]].cap) flow = edge[cur[i]].cap, index = i; 48 for(i = S; i != T; i = edge[cur[i]].v) 49 edge[cur[i]].cap -= flow, edge[cur[i]^1].cap += flow; 50 ans += flow; u = index; 51 } 52 for(i = cur[u]; ~i; i = edge[i].nxt) if(dis[edge[i].v] == dis[u] - 1 && edge[i].cap) break; 53 if(~i) { cur[u] = i; pre[edge[i].v] = u; u = edge[i].v; } 54 else { 55 int md = n + 1; 56 if(--gap[dis[u]] == 0) break; 57 for(i = head[u]; ~i; i = edge[i].nxt) 58 if(md > dis[edge[i].v] && edge[i].cap) md = dis[edge[i].v], cur[u] = i; 59 gap[dis[u] = md + 1]++; 60 u = pre[u]; 61 } 62 } 63 return ans; 64 } 65 66 int main() { 67 int n; LL w, sum = 0; 68 scanf("%d", &n); 69 S = 0; T = n + 1; 70 memset(head, -1, sizeof(head)); tot = 0; 71 for(int i = 1; i <= n; i++) 72 scanf("%lld", &w), Add(S, i, w); 73 for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%lld", &mp[i][j]), e[i] += mp[i][j], sum += mp[i][j]; 74 for(int i = 1; i <= n; i++) { 75 Add(i, T, e[i]); 76 for(int j = i + 1; j <= n; j++) { 77 Add(i, j, mp[i][j] * 2); Add(j, i, mp[i][j] * 2); 78 } 79 } 80 LL ans = ISAP(T + 1); 81 printf("%lld\n", sum - ans); 82 return 0; 83 }