kebab - 网络流最大流 + 区间离散化

Posted emcikem

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kebab - 网络流最大流 + 区间离散化相关的知识,希望对你有一定的参考价值。

传送门
和我上一篇博客的思路很像,区别在于每一个任务的(s_i,e_i)不一样,这题的(e_i - s_i)很大,所以无法直接全部进行建立
那么考虑对其进行离散化,也就是或对于区间[l,r]的作为一个结点,然后按照前面的方法去建立
对于第一层边的建立,容量就是烤肉数 * 单个烤肉的时间
第二层边的建立,对应的应该是(times[i] - times[i - 1] * m),其中(times)数组是离散化后的时间点,那么容量是时间段 * m
第三层边的建立,和第二层边一样

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 2000 + 5;
int n, m, s, t;
struct Edge{
    int to, next, w;
}e[N * N];
int head[N], tot = 1, cur[N]; //弧优化
void add(int u, int v, int w){
    e[++tot].to = v;
    e[tot].w = w;
    e[tot].next = head[u];
    head[u] = tot;
}
int dis[N];
int dfs(int u, int flow){ //保证了每次DFS都能找到增广路
    if(u == t) return flow;
    int sum = 0;
    for(int i = cur[u]; i && flow > 0; i = e[i].next){
        cur[u] = i;
        int v = e[i].to, w = e[i].w;
        if(w > 0 && dis[v] == dis[u] + 1){
            int t = dfs(v, min(flow, w)); //获取这条增广路的最小流量
            e[i].w -= t; e[i ^ 1].w += t; //减去最小流量,同时反向边加上最小流量
            flow -= t; sum += t;
        }
    }
    if(!sum) dis[u] = 0;//结果u无法到达终点,或者没有增广路,切断经过这个点的路径
    return sum;
}
bool bfs(){//分层判断是否有增广路
    memset(dis, 0, sizeof(dis));
    queue<int> q;
    q.push(s); dis[s] = 1; cur[s] = head[s];//弧优化
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i; i = e[i].next){
            int v = e[i].to, w = e[i].w;
            if(w > 0 && !dis[v]) {
                cur[v] = head[v];// v这个点从head[v]出发是可行的
                dis[v] = dis[u] + 1;//分层
                q.push(v);
                if(v == t) return 1;//已经到达增广路,直接返回
            }
        }
    }
    return dis[t];
}
ll Dinic(){
    ll max_flow = 0;
    while(bfs()){
        max_flow += dfs(s, 2e9);
    }
    return max_flow;
}
int st[N], ed[N], num[N], ti[N], times[N], k = 0; 
int main(){
    int n, m;
    while(~scanf("%d%d", &n, &m)){
        memset(head, 0, sizeof(head));
        tot = 1;
        k = 0;
        ll sum = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d%d", &st[i], &num[i], &ed[i], &ti[i]);
            times[++k] = st[i];
            times[++k] = ed[i];
            sum += num[i] * ti[i];
        }
        sort(times + 1, times + k + 1);
        k = unique(times + 1, times + k + 1) - times;//离散化
        s = 0, t = n + k + 1;
        for(int i = 1; i <= n; i++){//第一层边
            add(s, i, num[i] * ti[i]);
            add(i, s, 0);
        }

        for(int i = 1; i <= n; i++){//第二层边
            for(int j = 1; j <= k; j++){
                if(st[i] <= times[j - 1] && times[j] <= ed[i]){
                    add(i, n + j, 2e9);
                    add(n + j, i, 0);
                }
            }
        }
        for(int i = 1; i <= k; i++){// 第三层边
            add(n + i, t, (times[i] - times[i - 1]) * m);
            add(t, n + i, 0);
        }
        printf("%s
", Dinic() == sum ? "Yes" : "No");
    }
    return 0; 
}






以上是关于kebab - 网络流最大流 + 区间离散化的主要内容,如果未能解决你的问题,请参考以下文章

poj3680 Intervals 区间k覆盖问题 最小费用最大流 建图巧妙

POJ2047 Concert Hall Scheduling(最小费用最大流)

POJ3680:Intervals(离散化+最大流最小费用)

ACM-ICPC 2018 焦作赛区网络预赛 F. Modular Production Line (区间K覆盖-最小费用流)

P3358 最长k可重区间集问题

POJ 3680 Intervals(最小费用流)