1. 程式人生 > >【網路流24題】餐巾計劃(最小費用最大流)

【網路流24題】餐巾計劃(最小費用最大流)

題意

一個餐廳在相繼的 nn 天裡,每天需用的餐巾數不盡相同。假設第 ii 天需要 rir_i​​ 塊餐巾。餐廳可以購買新的餐巾,每塊餐巾的費用為 PP 分;或者把舊餐巾送到快洗部,洗一塊需 MM天,其費用為 FF 分;或者送到慢洗部,洗一塊需 NN 天,其費用為 SS 分(S<FS < F)。

每天結束時,餐廳必須決定將多少塊髒的餐巾送到快洗部,多少塊餐巾送到慢洗部,以及多少塊儲存起來延期送洗。但是每天洗好的餐巾和購買的新餐巾數之和,要滿足當天的需求量。

試設計一個演算法為餐廳合理地安排好 nn 天中餐巾使用計劃,使總的花費最小。

題解

【建模方法】 把每天分為二分圖兩個集合中的頂點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;
}