zoukankan      html  css  js  c++  java
  • 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
    ", Ans - dinic());
        return 0;
    }
    
  • 相关阅读:
    JS数组定义及详解
    JS中script词法分析
    JS函数 -- 功能,语法,返回值,匿名函数,自调用匿名函数,全局变量与局部变量,arguments的使用
    Java面试(1)-- Java逻辑运算符
    Java面试(3)-- Java关系运算符
    让 history 命令显示日期和时间
    mysql 权限管理
    docker基础
    docker 后台运行和进入后台运行的容器
    expect 自动输入密码
  • 原文地址:https://www.cnblogs.com/whenc/p/14027160.html
Copyright © 2011-2022 走看看