【网络流24题】餐巾计划(最小费用最大流)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pengwill97/article/details/82821774

题意

一个餐厅在相继的 n n 天里,每天需用的餐巾数不尽相同。假设第 i i 天需要 r i r_i ​​ 块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为 P P 分;或者把旧餐巾送到快洗部,洗一块需 M M 天,其费用为 F F 分;或者送到慢洗部,洗一块需 N N 天,其费用为 S S 分( S < F S < F )。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 n n 天中餐巾使用计划,使总的花费最小。

题解

【建模方法】
把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T。
1、从S向每个Xi连一条容量为ri,费用为0的有向边。
2、从每个Yi向T连一条容量为ri,费用为0的有向边。
3、从S向每个Yi连一条容量为无穷大,费用为p的有向边。
4、从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边。
5、从每个Xi向Yi+m(i+m<=N)连一条容量为无穷大,费用为f的有向边。
6、从每个Xi向Yi+n(i+n<=N)连一条容量为无穷大,费用为s的有向边。
求网络最小费用最大流,费用流值就是要求的最小总花费。
【建模分析】
这个问题的主要约束条件是每天的餐巾够用,而餐巾的来源可能是最新购买,也可能是前几天送洗,今天刚刚洗好的餐巾。每天用完的餐巾可以选择送到快洗部或慢洗部,或者留到下一天再处理。
经过分析可以把每天要用的和用完的分离开处理,建模后就是二分图。二分图X集合中顶点Xi表示第i天用完的餐巾,其数量为ri,所以从S向Xi连接容量为ri的边作为限制。Y集合中每个点Yi则是第i天需要的餐巾,数量为ri,与T连接的边容量作为限制。每天用完的餐巾可以选择留到下一天(Xi->Xi+1),不需要花费,送到快洗部(Xi->Yi+m),费用为f,送到慢洗部(Xi->Yi+n),费用为s。每天需要的餐巾除了刚刚洗好的餐巾,还可能是新购买的(S->Yi),费用为p。
在网络上求出的最小费用最大流,满足了问题的约束条件(因为在这个图上最大流一定可以使与T连接的边全部满流,其他边只要有可行流就满足条件),而且还可以保证总费用最小,就是我们的优化目标。

代码

普通费用流

#include "bits/stdc++.h"
using namespace std;
const int nmax = 5000;
const int INF = 0x3f3f3f3f;
typedef int valtype;
struct MCMF{
    valtype final_flow,final_cost;
    int tot,S,T;
    bool inque[nmax];
    int head[nmax], pre_edge[nmax],pre_index[nmax];
    valtype add_flow[nmax], dis[nmax];
    struct edge{int to,nxt;valtype cap,flow,cost;}e[nmax<<1];
    void init(int S, int T){
        memset(head,-1,sizeof head);
        tot = 0;
        this->S = S, this->T = T;
    }
    void add_edge(int u, int v, valtype cap, valtype cost){
        e[tot].to = v, e[tot].nxt = head[u], e[tot].flow = 0, e[tot].cap = cap, e[tot].cost = cost, head[u] = tot++;
        e[tot].to = u, e[tot].nxt = head[v], e[tot].flow = 0, e[tot].cap = 0, e[tot].cost = -cost, head[v] = tot++;
    }
    bool spfa(){
        for(int i = S;i<=T;++i) inque[i] = false, dis[i] = i == S?0:INF;
        queue<int> q; q.push(S), inque[S] = true, add_flow[S] = INF;
        while(!q.empty()){
            int u = q.front(); q.pop(); inque[u] = false;
            for(int i = head[u];i!=-1;i=e[i].nxt){
                int v = e[i].to;
                if(e[i].cap > e[i].flow && dis[u] + e[i].cost < dis[v]){
                    dis[v] = dis[u] + e[i].cost, pre_edge[v] = i, pre_index[v] = u;
                    add_flow[v] = min(add_flow[u],e[i].cap - e[i].flow);
                    if(!inque[v]) q.push(v),inque[v] = true;
                }
            }
        }
        return dis[T] != INF;
    }
    void mincost_mxflow() {
        final_cost = final_flow = 0;
        while(spfa()){
            final_flow += add_flow[T];
            final_cost += add_flow[T] * dis[T];
            int now = T;
            while(now != S){
                e[pre_edge[now]].flow += add_flow[T];
                e[pre_edge[now]^1].flow -= add_flow[T];
                now = pre_index[now];
            }
        }
    }
}mcmf;
int n, P, M, F, N ,S;
int r[nmax];
int main() {
//    freopen("in.txt", "r", stdin);
    scanf("%d %d %d %d %d %d", &n, &P, &M, &F, &N, &S);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &r[i]);
    }
    int s = 0, t = n * 2 + 1;
    mcmf.init(s, t);
    for(int i = 1; i <= n; ++i) {
        mcmf.add_edge(s, i, r[i], 0);
        mcmf.add_edge(i + n, t, r[i], 0);
        mcmf.add_edge(s, i + n, INF, P);
        if(i + 1 <= n) {
            mcmf.add_edge(i, i + 1, INF, 0);
        }
        if(i + M <= n) {
            mcmf.add_edge(i, i + n + M, INF, F);
        }
        if(i + N <= n) {
            mcmf.add_edge(i, i + n + N, INF, S);
        }
    }
    mcmf.mincost_mxflow();
    printf("%d\n", mcmf.final_cost);
    return  0;
}

zkw费用流



#include "bits/stdc++.h"
using namespace std;
const int nmax = 10005;
const int INF = 0x3f3f3f3f;
typedef int valtype;
struct zkwflow {
    struct edge {
        valtype cost, cap;
        int nxt, re, to;
    }e[nmax];
    int head[nmax], tot, vis[nmax], s, t;
    valtype ans, cost, maxflow;
    void init(int s, int t) {
        memset(head, -1, sizeof(head));
        tot = 0;
        ans = cost = maxflow = 0;
        this->s = s, this->t = t;
    }
    void add_edge(int u, int v, valtype cap, valtype cost) {
        e[tot].to = v;
        e[tot].cap = cap;
        e[tot].cost = cost;
        e[tot].re = tot + 1;
        e[tot].nxt = head[u];
        head[u] = tot++;
        e[tot].to = u;
        e[tot].cap = 0;
        e[tot].cost = -cost;
        e[tot].re = tot - 1;
        e[tot].nxt = head[v];
        head[v] = tot++;
    }
    int aug(int u, valtype f) {
        if(u == t) {
            ans += cost * f;
            maxflow += f;
            return f;
        }
        vis[u] = 1;
        valtype tmp = f;
        for(int i = head[u]; i != -1; i = e[i].nxt)
            if(e[i].cap && !e[i].cost && !vis[e[i].to]) {
                valtype delta = aug(e[i].to, tmp < e[i].cap ? tmp : e[i].cap);
                e[i].cap -= delta;
                e[e[i].re].cap += delta;
                tmp -= delta;
                if(!tmp) return f;
            }
        return f - tmp;
    }
    bool modlabel(int n) {
        valtype delta = INF;
        for(int u = 1; u <= n; u++)
            if(vis[u])
                for(int i = head[u]; i != -1; i = e[i].nxt)
                    if(e[i].cap && !vis[e[i].to] && e[i].cost < delta) delta = e[i].cost;
        if(delta == INF) return false;
        for(int u = 1; u <= n; u++)
            if(vis[u])
                for(int i = head[u]; i != -1; i = e[i].nxt)
                    e[i].cost -= delta, e[e[i].re].cost += delta;
        cost += delta;
        return true;
    }
    valtype costflow() {
        do {
            do {
                memset(vis, 0, sizeof(vis));
            }while(aug(s, INF));
        }while(modlabel(t));
        return ans;
    }
}zkw;
int n, P, M, F, N ,S;
int r[nmax];
int main() {
//    freopen("in.txt", "r", stdin);
    scanf("%d %d %d %d %d %d",&n, &P, &M, &F, &N, &S);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &r[i]);
    }
    int s = n * 2 + 1, t = n * 2 + 2;
    zkw.init(s, t);
    for(int i = 1; i <= n; ++i) {
        zkw.add_edge(s, i, r[i], 0);
        zkw.add_edge(i + n, t, r[i], 0);
        zkw.add_edge(s, i + n, INF, P);
        if(i + 1 <= n) {
            zkw.add_edge(i, i + 1, INF, 0);
        }
        if(i + M <= n) {
            zkw.add_edge(i, i + n + M, INF, F);
        }
        if(i + N <= n) {
            zkw.add_edge(i, i + n + N, INF, S);
        }
    }
    int finalans = zkw.costflow();
    printf("%d\n", finalans);
    return  0;
}

猜你喜欢

转载自blog.csdn.net/pengwill97/article/details/82821774