一道最小割的题。
所有跟源点连边的点代表被解雇了,跟汇点连边的代表没被解雇。
因为割掉每一条边代表付出一定的代价,所以应该先把正收益加起来,然后再减去最小割。
那么对于所有解雇收益为正的点,从汇点连一条容量为该收益的边,割掉这条边代表不解雇这个人了,付出的代价就是这个收益;对于收益为负的点,向汇点连一条容量为该收益的绝对值的边,割掉他代表解雇这个人,付出的代价就是这个收益。
如果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 }