POJ2987 Firing

嘟嘟嘟

一道最小割的题。

所有跟源点连边的点代表被解雇了,跟汇点连边的代表没被解雇。

因为割掉每一条边代表付出一定的代价,所以应该先把正收益加起来,然后再减去最小割。

那么对于所有解雇收益为正的点,从汇点连一条容量为该收益的边,割掉这条边代表不解雇这个人了,付出的代价就是这个收益;对于收益为负的点,向汇点连一条容量为该收益的绝对值的边,割掉他代表解雇这个人,付出的代价就是这个收益。

如果y是x的上司,就从y向x连一条容量为INF的边,因为这个关系是无法改变的,所以改变他的代价是INF。

最后跑网络流求最小割。(别忘开long long)

对于第一问,我们只用在残图上跑一遍,能跑到的点就是解雇人数。因为所有和源点相连的代表被解雇了。

很多题解都说是最大权闭合子图,建图也确实是这么建。然而我这觉得那么理解起来比较困难。

还有一件事,这题我WA了n多发,然后跑残图的时候把e[i].cap > 0这个条件删了就莫名AC了……求助各路神仙。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 5e3 + 5;
 21 const int maxe = 2e6 + 5;
 22 inline ll read()
 23 {
 24     ll ans = 0;
 25     char ch = getchar(), last = ' ';
 26     while(!isdigit(ch)) {last = ch; ch = getchar();}
 27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 28     if(last == '-') ans = -ans;
 29     return ans;
 30 }
 31 inline void write(ll x)
 32 {
 33     if(x < 0) x = -x, putchar('-');
 34     if(x >= 10) write(x / 10);
 35     putchar(x % 10 + '0');
 36 }
 37 
 38 int n, m, t;
 39 ll sum = 0;
 40 
 41 struct Edge
 42 {
 43     int nxt, from, to;
 44     ll cap, flow;
 45 }e[maxe];
 46 int head[maxn], ecnt = -1;
 47 void addEdge(int x, int y, ll w)
 48 {
 49     e[++ecnt] = (Edge){head[x], x, y, w, 0};
 50     head[x] = ecnt;
 51     e[++ecnt] = (Edge){head[y], y, x, 0, 0}; 
 52     head[y] = ecnt;
 53 }
 54 
 55 int dis[maxn];
 56 bool bfs()
 57 {
 58     Mem(dis, 0); dis[0] = 1;
 59     queue<int> q; q.push(0);
 60     while(!q.empty())
 61     {
 62     int now = q.front(); q.pop();
 63     for(int i = head[now]; i != -1; i = e[i].nxt)
 64     {
 65         if(!dis[e[i].to] && e[i].cap > e[i].flow)
 66         {
 67         dis[e[i].to] = dis[now] + 1;
 68         q.push(e[i].to);
 69         }
 70     }
 71     }
 72     return dis[t];
 73 }
 74 int cur[maxn];
 75 ll dfs(int now, ll res)
 76 {
 77     if(now == t || res == 0) return res;                    
 78     ll flow = 0, f;
 79     for(int &i = cur[now]; i != -1; i = e[i].nxt)
 80     {
 81     if(dis[e[i].to] == dis[now] + 1 && (f = dfs(e[i].to, min(res, e[i].cap - e[i].flow))) > 0)
 82     {
 83         e[i].flow += f; e[i ^ 1].flow -= f;
 84         flow += f; res -= f;
 85         if(res == 0) break;
 86     }
 87     }
 88     return flow;
 89 }
 90 
 91 ll minCut()
 92 {
 93     ll flow = 0;
 94     while(bfs())
 95     {
 96       memcpy(cur, head, sizeof(head));  //这么写方便多了
 97       flow += dfs(0, (ll)INF * (ll)INF);
 98     }
 99     return flow;
100 }
101 
102 int ans = 0;
103 bool vis[maxn];
104 void dfs2(int now)
105 {
106     vis[now] = 1; ans++;
107     for(int i = head[now]; i != -1; i = e[i].nxt)
108     {
109       if(e[i].to != t && !vis[e[i].to] && e[i].cap > e[i].flow) 
110     dfs2(e[i].to);
111     }
112 }
113 
114 int main()
115 {
116   Mem(head, -1);
117     n = read(); m = read(); t = n + 1;
118     for(int i = 1; i <= n; ++i)
119     {
120     int x = read();
121     if(x > 0) addEdge(0, i, x), sum += x;
122     else addEdge(i, t, -x);
123     }
124     for(int i = 1; i <= m; ++i)
125     {
126     int x = read(), y = read();
127     addEdge(x, y, (ll)INF * (ll)INF);
128     }
129     ll x = minCut();
130     dfs2(0);
131     write(ans - 1), space; write(sum - x), enter;
132     return 0;
133 }
View Code
原文地址:https://www.cnblogs.com/mrclr/p/9828917.html