拿到题后根据标签

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。
但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
于是整理得,我们需要支持以下操作:
1.将脏餐巾以f元/条送到快洗部,过m天后,干净餐巾送回来使用
2.将脏餐巾以s元/条送到慢洗部,过n天后,干净餐巾送回来使用
3.延期送洗(可能会出现之后餐巾需求过少,并不需要所有餐巾的情况)
4.以p元/条购买新的餐巾
于是本题第一个难点就来了:如何处理脏餐巾和干净餐巾?

我们可以想到,每天开始的时候只有干净的餐巾可使用,每天结束的时候仅有脏的餐巾需要操作。于是将每天拆成两个点:起始点与结束点,分别处理不同时间段所需操作。

于是也可以想到:
1.送到快洗部属于结束点操作,连向i+m天后的起始点,费用为f(表示餐巾洗好了,可使用)
2.送到慢洗部属于结束点操作,连向i+n天后的起始点,费用为s
3.延期送洗属于结束点操作,连向i+1的起始点,不需费用。
4.购买新的餐巾属于起始点操作,目前并没有确定从哪连的,但费用为p。

以上操作流量均为[网络流24题] 餐巾计划问题_ios

好了,到这里你有没有发现以上操作有什么共同点?
没错,他们都是连向起始点的有向边!

事实上,网络流24题的全称为“网络流与线性规划24题”,这也就告诉我们: 网络流的建图一定有顺序的,建的边一定是沿源点流向汇点,否则图就会不流通。

所以现在我们就弄明白了:我们应该将源点连接每天的结束点,而不是他们的起始点;起始点连向他们的汇点,容量为这天所需的餐巾数量,费用为0。

此外购买餐巾直接从源点购买即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e4+10;
const int maxm = 1e5+10;
const int inf = 0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow,cost;
}edge[maxm];
int head[maxn],tol,pre[maxn],dis[maxn],N,x[maxn];
bool vis[maxn];
void add_edge(int u,int v,int cap,int cost)
{
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;

    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool Spfa(int s,int t)
{
    queue<int>q;
    while(!q.empty())
        q.pop();
    memset(dis,inf, sizeof(dis));
    memset(vis,false, sizeof(vis));
    memset(pre,-1, sizeof(pre));
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost)
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)
        return false;
    return true;
}
void minCostmaxFlow(int s,int t)
{
    int flow = 0,cost = 0;
    while(Spfa(s,t))
    {
        //cout << "!!!" << endl;
        int Min = inf;
        for(int i = pre[t];i != -1;i = pre[edge[i^1].to])
        {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t];i != -1;i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    cout << cost << endl;
}
signed main()
{
    //freopen("in","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    memset(head,-1, sizeof(head));
    int n,P,M,F,N,S,s = 0,t;
    cin >> n >> P >> M >> F >> N >> S;
    t = n*2+10;
    for(int i = 1;i <= n; i++)
    {
        cin >> x[i];
        add_edge(s,i,x[i],0); //源点加边
        add_edge(i+n,t,x[i],0);//汇点加边
        add_edge(s,i+n,inf,P);//从源点购买餐巾
        if(i + 1 <= n)
            add_edge(i,i+1,inf,0);//拖延到明天
        if(i + M <= n)
            add_edge(i,i+n+M,inf,F);//快洗
        if(i + N <= n)
            add_edge(i,i+n+N,inf,S);//慢洗
    }
    minCostmaxFlow(s,t);
    return 0;
}