Loading

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 }

 

posted @ 2017-02-18 10:19  Shadowdsp  阅读(202)  评论(0编辑  收藏  举报