1. 程式人生 > 實用技巧 >【費用流】P2517 [HAOI2010]訂貨

【費用流】P2517 [HAOI2010]訂貨

P2517 [HAOI2010]訂貨

思路:

(為啥這題不用拆點而UVA11613 Acme Corporation需要?兩道題有什麼共同點和區別?)

從這題可以窺見費用流問題的總體模型:有進有出,進出口在網路兩端(超級源和超級匯),網路內部通過各種約束關係關聯起來。

本題中,就是每個月\(i\)進貨,費用為\(D_i\),且沒有限制進貨數量故可以無限進貨,所以是一條容量為INF且單位費用為\(D_i\)的邊,故建邊addf(s, i, INF, p)

就是每個月\(i\)銷售,銷售沒有費用,但有需求量的限制,超過需求量是賣不出去的,所以是一條容量為\(U_i\)(程式碼中為p)且單位費用為\(0\)

的邊,建邊addf(i, t, p, 0)

除第\(n\)個月外,每個月賣不完的貨可以儲存到倉庫中,等到下個月再賣,倉庫容量為\(vol\),單位儲存費用為\(cost\),故除第\(n\)個月外每個月\(i\)向下一個月\(i+1\)連邊,容量為\(vol\),費用為\(cost\),建邊addf(i, i + 1, vol, cost)

然後跑最小費用最大流即可。

int cnt_e = 0, head[maxn], n, m;
int s, t;
LL dis[maxn], d[maxn];
int pre[maxn];
bool inq[maxn];
LL maxflow, mincost;

void addf(int u, int v, LL w, LL c) {
    //費用流建圖
    e[++cnt_e].next = head[u]; e[cnt_e].from = u; e[cnt_e].to = v; e[cnt_e].w = w; e[cnt_e].cost = c; head[u] = cnt_e;
    e[++cnt_e].next = head[v]; e[cnt_e].from = v; e[cnt_e].to = u; e[cnt_e].w = 0; e[cnt_e].cost = -c; head[v] = cnt_e;
}

bool spfa() {
    queue<int> q;
    mem(dis, INF);
    mem(d, 0);
    mem(inq, 0);
    q.push(s); dis[s] = 0; inq[s] = 1;
    d[s] = INF;
    while (!q.empty()) {
        int u = q.front();
        inq[u] = 0; q.pop();
        for (int i = head[u]; i; i = e[i].next) {
            if (e[i].w <= 0) continue;
            int v = e[i].to;
            if (dis[u] + e[i].cost < dis[v]) {
                dis[v] = dis[u] + e[i].cost;
                //費用最短路
                pre[v] = i;
                d[v] = min(d[u], e[i].w);
                //維護路徑上的最小殘量
                if (!inq[v]) {
                    inq[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    if (!d[t]) return 0;
    return 1;
}

void MCMF() {
    while (spfa()) {
        for (int x = t; x != s; x = e[pre[x] ^ 1].to) {
            e[pre[x]].w -= d[t];
            e[pre[x] ^ 1].w += d[t];
        }
        maxflow += d[t];
        mincost += d[t] * dis[t];
        //流量乘上最小單位流量費用即總流量費用
    }
}

int main() {
    ios::sync_with_stdio(false);
    int cost, vol;
    cnt_e = 1;
    cin >> n >> cost >> vol;
    s = n + 1; t = n  + 2;
    for (int i = 1; i <= n; i++) {
        int p; cin >> p;
        addf(i, t, p, 0);
        //需求量為p,最多賣掉p,故容量p
        //賣出不需要花費,費用為0
    }
    for (int i = 1; i <= n; i++) {
        int p; cin >> p;
        addf(s, i, INF, p);
        //可以無限進貨,故容量INF
        //進價為p,費用為p
    }
    //左點連右點,容量vol,費用為cost
    //在相鄰的月間連,不是兩兩之間都連qwq
    for (int i = 1; i < n; i++) {
        addf(i, i + 1, vol, cost);
    }
    MCMF();
    cout << mincost;
    return 0;
}