【費用流】P2517 [HAOI2010]訂貨
阿新 • • 發佈:2020-11-01
思路:
(為啥這題不用拆點而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; }