[费用流]LOJ#6079. 「2017 山东一轮集训 Day7」养猫

Description

你养了一只猫,为了让它快乐地成长,你需要合理地安排它每天的作息时间。假设一天分为 n 个时刻,猫在每个时刻要么是吃东西,要么是睡觉。在第i个时刻,假如猫是去吃东西,那么它能获得愉悦值 ei ​,假如是去睡觉,那么能获得的愉悦值为 si

猫要成长,不仅仅需要快乐,还需要健康的作息。经过研究,对于每一个连续的长度为 k 的作息区间,即所有的时刻区间[i,i+k1],1ink+1,猫都要至少有 ms 的时刻用来睡觉, me 的时刻用来吃东西,这样猫才能健康成长。

现在你想合理地安排一天中的这 n 个时刻,使得猫在能健康成长的前提下,获得尽量多的愉悦值。

Solution

**#include <bits/stdc++.h>
using namespace std;

const int INF = 1 << 30;
const int N = 1010;
typedef long long ll;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
inline void read(int &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

struct edge {
    int to, next;
    int cap, cost;
    edge (int t = 0, int n = 0, int c = 0, int co = 0):to(t), next(n), cap(c), cost(co) {}
};
edge G[N << 3];
int head[N];
ll dist[N];
int vis[N], pre[N];
int s[N], e[N];
int ans[N];
int Gcnt, n, k, ms, me;
ll Ans;
queue<int> Q;

inline void AddEdge(int from, int to, int cap, int cost) {
    G[++Gcnt] = edge(to, head[from], cap, cost); head[from] = Gcnt;
    G[++Gcnt] = edge(from, head[to], 0, -cost); head[to] = Gcnt;
}
inline bool SPFA(int S, int T) {
    static int x;
    memset(dist, 0x3f, sizeof dist);
    Q.push(S); dist[S] = 0;
    while (!Q.empty()) {
        x = Q.front(); Q.pop();
        vis[x] = false;
        for (int i = head[x]; i; i = G[i].next) {
            edge &e = G[i];
            if (e.cap && dist[x] + e.cost < dist[e.to]) {
                pre[e.to] = i;
                dist[e.to] = dist[x] + e.cost;
                if (!vis[e.to]) {
                    vis[e.to] = true; Q.push(e.to);
                }
            }
        }
    }
    return dist[T] != dist[0];
}
inline ll Flow(int S, int T) {
    ll Cost = 0; int f;
    while (SPFA(S, T)) {
        f = INF;
        for (int i = T; i != S; i = G[pre[i] ^ 1].to)
            f = min(f, G[pre[i]].cap);
        for (int i = T; i != S; i = G[pre[i] ^ 1].to) {
            G[pre[i]].cap -= f;
            G[pre[i] ^ 1].cap += f;
        }
        Cost += f * dist[T];
    }
    return Cost;
}

int main(void) {
    freopen("1.in", "r", stdin);
    Gcnt = 1;
    read(n); read(k); read(ms); read(me);
    for (int i = 1; i <= n; i++) read(s[i]);
    for (int i = 1; i <= n; i++) read(e[i]);
    for (int i = 1; i <= n; i++) {
        Ans += s[i]; s[i] -= e[i];
    }
    for (int i = 1; i <= n; i++) {
        if (i + k <= n) AddEdge(i, i + k, 1, s[i]);
        else AddEdge(i, n + 2, 1, s[i]);
        ans[i] = Gcnt - 1;
        if (i < n) AddEdge(i, i + 1, k - ms - me, 0);
        else AddEdge(i, n + 2, k - ms - me, 0);
    }
    AddEdge(n + 1, n + 3, k - ms, 0);
    for (int i = 1; i <= k; i++)
        AddEdge(n + 3, i, INF, 0);
    Ans -= Flow(n + 1, n + 2);
    printf("%lld\n", Ans);
    for (int i = 1; i <= n; i++)
        if (G[ans[i]].cap) putchar('S');
        else putchar('E');
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值