洛谷 P1251 餐巾计划问题 (费用流)网络流24题(3)

洛谷 P1251 餐巾计划问题 (费用流)网络流24题(3)

链接
题意:有一个餐厅,每天需要一定数量的餐巾 x [ i ] x[i] x[i],每天可以购买餐巾,花费 p p p,或者送清洗店,花费 d a y 1 day_1 day1 c o s t 1 cost_1 cost1或者 d a y 2 day_2 day2 c o s t 2 cost_2 cost2,除了 x x x,对于每一天其他变量都是一样的
思路:我们考虑把每一天拆点,分成早上和晚上,记早上是 i i i,晚上是 i ′ i' i
1.对于每一天,我们可以购买,所以直接连接 s → i ( I N F , p ) s\to i (INF,p) si(INF,p),代表购买
2.因为每一天需要使用 x [ i ] x[i] x[i],所以我们还需要连接每一天到汇点,由于晚上的毛巾代表的是脏毛巾,所以我们要连接的是 i → t ( x [ i ] , 0 ) i\to t(x[i],0) it(x[i],0)
3.因为每天会产生 x [ i ] x[i] x[i]的脏毛巾,我们感性考虑应该是从 i → i ′ ( x [ i ] , 0 ) i\to i'(x[i],0) ii(x[i],0),但是这是不对的,因为这样一来我们就需要每天有 2 ∗ x [ i ] 2*x[i] 2x[i]的毛巾才能满足到汇点和到 i ′ i' i的流量,所以我们直接 s → i ′ ( x [ i ] , 0 ) s\to i'(x[i],0) si(x[i],0)
4.每一天,我们可以送至两个洗衣店,所以我们可以把每天晚上脏毛巾送至 i + d a y 1 / 2 i+day_{1/2} i+day1/2的干净毛巾,有 i ′ → i + d a y 1 / 2 ( I N F , c o s t 1 / 2 ) i'\to i+day_{1/2}(INF,cost_{1/2}) ii+day1/2(INF,cost1/2)
5.我们可以累计毛巾,所以我们选择留脏毛巾,或者干净毛巾,效果是一样的,因为每天购买的价格一样,我选择留脏毛巾,有 i ′ → ( i + 1 ) ′ ( I N F , 0 ) i'\to (i+1)'(INF,0) i(i+1)(INF,0)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e3+15, INF = 0x3f3f3f3f3f3f3f3f;
int head[N], idx = 1;
struct Edge{int to, nxt, d, c;}e[10000010];
void add(int u, int v, int d, int c)
{
    e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx, e[idx].d = d, e[idx].c = c;
    e[++idx].to = u, e[idx].nxt = head[v], head[v] = idx, e[idx].d = 0, e[idx].c = -c;
}
int n, p, m, f, n1, s1;
int dis[N], inq[N], incf[N], pre[N], s, t, maxflow, mincost;

bool spfa()
{
    queue<int> Q;
    memset(dis, 0x3f, sizeof (dis));
    memset(inq, 0, sizeof (inq));
    Q.push(s);
    dis[s] = 0;
    inq[s] = 1;
    incf[s] = INF;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i = e[i].nxt) {
            if (!e[i].d) continue;
            int v = e[i].to, d = e[i].c;
            if (dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                incf[v] = min(incf[u], e[i].d);
                pre[v] = i;
                if (!inq[v]) {inq[v] = 1; Q.push(v);}
            }
        }
    }
    return dis[t] != 0x3f3f3f3f3f3f3f3f;
}

int MCMF()
{
    while (spfa()) {
        int x = t;
        maxflow += incf[t];
        mincost += dis[t] * incf[t];
        int i;
        while (x != s) {
            i = pre[x];
            e[i].d -= incf[t];
            e[i ^ 1].d += incf[t];
            x = e[i ^ 1].to;
        }
    }return mincost;
}

signed main()
{
    cin >> n;
    t = n + n + 10;
    for (int i = 1; i <= n; i++) {
        int x; cin >> x;
        add(i, t, x, 0);
        add(s, i + n, x, 0);
    }
    cin >> p >> m >> f >> n1 >> s1;
    for (int i = 1; i <= n; i++) {
        add(s, i, INF, p);
        if (i != n) add(i + n, i + 1 + n, INF, 0);
        if (i + m <= n) add(i + n, i + m, INF, f);
        if (i + n1 <= n) add(i + n, i + n1, INF, s1);
    }
    cout << MCMF();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值