Luogu4177 [CEOI2008]order(网络流)题解
算法
最大权闭合子图
思路
首先,如果没有租借这个条件,就是一个最大权闭合子图的模板了,让我们背诵最大权闭合子图的建图方法:
- 将源点与正权点连边,流量为权值;
- 将负权点与汇点连边,流量为权值相反数(正数);
- 将原图中的边相连,流量为正无穷。
- 最大权闭合子图$ =$ 原图正权点权值和$ -$ 最小割
思考:我们为什么要把原图中的边设为正无穷?
\(A:\)将某点与源点的边割去表示将其划分在不选集合中,将与汇点的边割去表示在选择集合中,而原图中的边是不能割掉的,所以设为正无穷,这样我们再求最小割的时候就不会把ta割掉了。
那么,我们如何解救这道题呢?
想一想租借这个条件为什么会使原本的算法错误,因为有可能租借某个机器来满足工作需求比购买ta更划算,而租借机器是对其他的工作没有影响的。所以,我们可以把工作与机器之间的边的流量设成租借费用,这样就可以通过割掉租借边来把某个工作划分到选择集合中而不影响其他的工作。
参考代码
/*
* @Author: When_C
* @Date: 2020-11-23 17:34:21
* @Last Modified by: When_C
* @Last Modified time: 2020-11-23 18:35:43
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxm = 3e6 + 10,maxn = 1e5 + 10;
int n,m,head[maxn],num,S,T,Ans;
struct Edge{
int then,to,val;
}e[maxm];
void add(int u, int v, int val){
e[++num] = (Edge){head[u], v, val}; head[u] = num;
e[++num] = (Edge){head[v], u, 0}; head[v] = num;
}
int vis[maxn],dep[maxn];
int bfs(int st, int en){
memset(vis,0,sizeof(vis));
for(int i = 1; i <= n + m + 2; ++ i) dep[i] = INF;
queue<int> q;
vis[st] = 1, dep[st] = 1, q.push(st);
while(!q.empty()){
int u = q.front();
vis[u] = 0, q.pop();
for(int i = head[u]; i; i = e[i].then){
int v = e[i].to;
if(dep[v] == INF && e[i].val){
dep[v] = dep[u] + 1;
if(!vis[v]){vis[v] = 1; q.push(v);}
}
}
}
return (dep[en] != INF);
}
int cur[maxn];
int dfs(int u, int sum){
if(u == T) return sum;
int now = 0;
for(int &i = cur[u]; i; i = e[i].then){
int v = e[i].to;
if(dep[v] == dep[u] + 1 && e[i].val){
int a = dfs(v, min(e[i].val, sum - now));
now += a;
e[i].val -= a, e[i ^ 1].val += a;
if(now == sum) return now;
}
}
return now;
}
int dinic(){
int maxflow = 0;
while(bfs(S, T)){
memcpy(cur,head,sizeof(head));
maxflow += dfs(S, INF);
}
return maxflow;
}
int main(){
scanf("%d%d", &n, &m);
num = 1, S = n + m + 1; T = S + 1;
for(int i = 1; i <= n; ++ i){
int x,t; scanf("%d%d", &x, &t);
add(S, i, x); Ans += x;
while(t--){
int a,b; scanf("%d%d", &a, &b);
add(i, a + n, b);
}
}
for(int i = 1; i <= m; ++ i){
int y; scanf("%d", &y);
add(i + n, T, y);
}
printf("%d\n", Ans - dinic());
return 0;
}