1. 程式人生 > 實用技巧 >Luogu4177 [CEOI2008]order(網路流)題解

Luogu4177 [CEOI2008]order(網路流)題解

演算法

最大權閉合子圖

思路

首先,如果沒有租借這個條件,就是一個最大權閉合子圖的模板了,讓我們背誦最大權閉合子圖的建圖方法:

  1. 將源點與正權點連邊,流量為權值;
  2. 將負權點與匯點連邊,流量為權值相反數(正數);
  3. 將原圖中的邊相連,流量為正無窮。
  4. 最大權閉合子圖$ =$ 原圖正權點權值和$ -$ 最小割

思考:我們為什麼要把原圖中的邊設為正無窮?

\(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;
}