网络流24题- 餐巾计划 (最小费用最大流)

题意:

一个餐厅在相继的 n 天里,每天需用的餐巾数不尽相同。假设第 i 天需要 ri块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为P,

或者把旧餐巾送到快洗部,洗一块需 M 天,其费用为 F 分;或者送到慢洗部,洗一块需 N ,其费用为 S 分(S<F)。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 n 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。

链接:  https://loj.ac/problem/6008

输入:

第1行有6个整数,n,P,M,F,S,N,含义与上面相对应,

接下来的n行是每天需要的餐巾数

输出格式:

将餐厅在相继的 n 天里使用餐巾的最小总花费输出。

思路:

建图方式:

将 i 点拆成 i1 和 i2 ,其中 i1 代表每天的餐巾数,i2 代表每天可以得到的餐巾。

将源点 S 与 i1 相连,容量为所需要的餐巾数,费用为0,将 i2 与汇点 T 相连,容量为所需要的餐巾数,费用为0

由于每天可以购买新的餐巾,费用为P,所以将 S 与 i2 相连,容量为INF,费用为P

用掉的脏餐巾可以累积,所以对于每个 i1 < n ,连接 i1-1 与 i1 ,容量为INF,费用为0

每天得到的餐巾可以由快洗或者慢洗得到,所以将每个 i1 与若干天后他能清洗完送达到的那个 i2 相连,容量为INF,费用为快洗或慢洗所需的费用

然后再跑一遍最小费用最大流

代码:

写法1:快点

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 0x7fffffff;
typedef long long ll;
int head[MAXN], tot, s, t, n;
struct node
{
    int to, nxt, flow, cost;
} e[MAXN];
void add(int x, int y, int z, int c)
{
    e[tot].to = y;
    e[tot].nxt = head[x];
    e[tot].flow = z;
    e[tot].cost = c;
    head[x] = tot++;
}
void add_edge(int x, int y, int z, int c)
{
    add(x, y, z, c);
    add(y, x, 0, -c);
}
int dis[MAXN];
bool visit[MAXN];
int cost = 0;
bool spfa()
{
    queue<int> q;
    memset(visit, 0, sizeof(visit));
    for (int i = 0; i <= 2 * n + 1; i++)
        dis[i] = INF;
    dis[s] = 0;
    q.push(s);
    while (q.size())
    {
        int u = q.front();
        q.pop();
        visit[u] = 0;
        for (int i = head[u]; ~i; i = e[i].nxt)
        {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].flow)
            {
                dis[v] = dis[u] + e[i].cost;
                if (!visit[v])
                {
                    q.push(v);
                    visit[v] = 1;
                }
            }
        }
    }
    return dis[t] != INF;
}
int dfs(int u, int flow)
{
    if (u == t)
    {
        visit[t] = 1;
        return flow;
    }
    int f = 0;
    visit[u] = 1;
    for (int i = head[u]; ~i; i = e[i].nxt)
    {
        int v = e[i].to;
        if ((!visit[v] || v == t) && e[i].flow && dis[v] == dis[u] + e[i].cost)
        {
            int d = dfs(v, min(flow - f, e[i].flow));
            if (d)
            {
                cost += e[i].cost * d;
                e[i].flow -= d;
                e[i ^ 1].flow += d;
                f += d;
                if (f == flow)
                    break;
            }
        }
    }
    return f;
}
void mcmf()
{
    while (spfa())
    {
        // printf("*");
        visit[t] = 1;
        while (visit[t])
        {
            memset(visit, 0, sizeof(visit));
            dfs(s, INF);
        }
    }
}
int main()
{
    int p, f_m, f_p, s_n, s_p;
    scanf("%d%d%d%d%d%d", &n, &p, &f_m, &f_p, &s_n, &s_p);
    memset(head, -1, sizeof(head));
    s = 0, t = 2 * n + 1;
    for (int i = 1; i <= n; i++)
    {
        int need;
        scanf("%d", &need);
        add_edge(s, i, need, 0);
        add_edge(i + n, t, need, 0);
    }
    for (int i = 1; i <= n; i++)
    {
        add_edge(s, i + n, INF, p);
    }
    for (int i = 1; i < n; i++)
        add_edge(i, i + 1, INF, 0);
    for (int i = 1; i + f_m <= n; i++)
        add_edge(i, i + f_m + n, INF, f_p);
    for (int i = 1; i + s_n <= n; i++)
        add_edge(i, i + n + s_n, INF, s_p);
    mcmf();
    printf("%d\n", cost);
    return 0;
}
View Code

 

 

 

 

写法2:慢点

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 0x7fffffff;
typedef long long ll;
int head[MAXN], tot, s, t, n, a[200][200];
struct node {
    int to, nxt, flow, cost;
} e[MAXN];
void add(int x, int y, int z, int c) {
    e[tot].to = y;
    e[tot].nxt = head[x];
    e[tot].flow = z;
    e[tot].cost = c;
    head[x] = tot++;
}
void add_edge(int x, int y, int z, int c) {
    add(x, y, z, c);
    add(y, x, 0, -c);
}
int dis[MAXN], q[MAXN], l, r, pre[MAXN], path[MAXN], visit[MAXN];
bool spfa() {
    for (int i = 0; i <= 2 * n + 1; i++) dis[i] = INF;
    for (int i = 0; i <= 2 * n + 1; i++) pre[i] = -1;
    l = 0, r = 0;
    dis[s] = 0;
    q[++r] = s;
    while (l != r) {
        int u = q[++l];
        for (int i = head[u]; ~i; i = e[i].nxt) {
            int v = e[i].to;
            if (e[i].flow && dis[u] + e[i].cost < dis[v]) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = u, path[v] = i;
                if (!visit[v])
                    q[++r] = v, visit[v] = 1;
            }
        }
        visit[u] = 0;
    }
    if (pre[t] == -1)
        return false;
    return true;
}
int mcmf() {
    int f = 0, cost = 0;
    while (spfa()) {
        int min_f = INF;
        for (int i = t; i != s; i = pre[i]) {
            if (e[path[i]].flow < min_f)
                min_f = e[path[i]].flow;
        }
        f += min_f;
        cost += dis[t] * min_f;
        for (int i = t; i != s; i = pre[i]) {
            e[path[i]].flow -= min_f;
            e[path[i] ^ 1].flow += min_f;
        }
    }
    return cost;
}
int main() {
    int p, f_m, f_p, s_n, s_p;
    scanf("%d%d%d%d%d%d", &n, &p, &f_m, &f_p, &s_n, &s_p);
    memset(head, -1, sizeof(head));
    s = 0, t = 2 * n + 1;
    for (int i = 1; i <= n; i++) {
        int need;
        scanf("%d", &need);
        add_edge(s, i, need, 0);
        add_edge(i + n, t, need, 0);
    }
    for (int i = 1; i <= n; i++) {
        add_edge(s, i + n, INF, p);
    }
    for (int i = 1; i < n; i++) add_edge(i, i + 1, INF, 0);
    for (int i = 1; i + f_m <= n; i++) add_edge(i, i + f_m + n, INF, f_p);
    for (int i = 1; i + s_n <= n; i++) add_edge(i, i + n + s_n, INF, s_p);
    printf("%d\n", mcmf());
    return 0;
}
View Code

 

posted @ 2020-03-03 13:56  grass_lin  阅读(197)  评论(0编辑  收藏  举报