题意
一个餐厅在相继的 n n n 天里,每天需用的餐巾数不尽相同。假设第 i i i 天需要 r i r_i ri 块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为 P P P 分;或者把旧餐巾送到快洗部,洗一块需 M M M天,其费用为 F F F 分;或者送到慢洗部,洗一块需 N N N 天,其费用为 S S S 分( S < F S < F S<F)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 n n n 天中餐巾使用计划,使总的花费最小。
题解
【建模方法】
把每天分为二分图两个集合中的顶点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;
}