【費用流+正負費用處理】UVA11613 Acme Corporation
阿新 • • 發佈:2020-11-02
思路
對於第\(i\)個月,貨物來源有:當月生產(從源點流入),以及之前的月份流入。因為有保質期,這意味著第\(i-1\)個月留下來的貨物裡保質期會各不相同,可能其中一部分是第\(i\)個月就過期無法賣出的,這一部分貨物顯然不能流入第\(i\)個月。
如何連邊才能體現“在保質期內賣出”?這就需要將一個月份的流入和流出分別處理,也就是拆成入點\(i\)和出點\(i+n\)。這樣,假設在第\(i\)個月生產的貨物保質期為\(keep[i]\),那麼它就可以在第\(i\),\(i+1\),\(i+2\),...,\(i+keep[i]\)月賣出,具體建邊就是\(i→i+n\)
由於費用有正有負(生產成本和盈利),比板子多設一個條件if(dis[t]≥0) return 0;
,即當費用為正時停止增廣。最後答案(求的是盈利)再取一次負即可。
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 (dis[t] >= 0) return 0; //費用為正時停止增廣 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 kase; cin >> kase; for(int kk=1;kk<=kase;kk++) { maxflow = mincost = 0; mem(e, 0); mem(head, 0); mem(pre, 0); cnt_e = 1; int cost; //1個月cost元 cin >> n >> cost; s = n * 2 + 1; t = n * 2 + 2; for (int i = 1; i <= n; i++) { int a, b, c, d; cin >> a >> b >> c >> d >> keep[i]; //生產成本,最大生產量,售價,最大銷售量 addf(s, i, b, a); for (int j = i; j <= min(n, i + keep[i]); j++) { addf(i, n + j, b, (j - i) * cost); } addf(i + n, t, d, -c); } MCMF(); cout << "Case " << kk << ": " << -mincost << endl; } return 0; }