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 }

 

转载于:https://www.cnblogs.com/fightfordream/p/6371021.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值