【费用流】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;
}