Gym 101190D,上下界区间覆盖

Delight for a Cat

A cat is going on an adventure.
Each hour, the cat can be either sleeping or eating. The cat cannot be doing both actions at the same
hour, and the cat is doing exactly one of these actions for the whole hour.
For each of the next n hours, the amount of delight the cat is getting if it is sleeping or eating during
that hour is known. These amounts can be different for each hour.
An integer time period k is also known. Among every k consecutive hours, there should be at least
ms hours when the cat is sleeping, and at least me hours when the cat is eating. So, there are exactly
n − k + 1 segments of k hours for which this condition must be satisfied.
Find the maximum total amount of delight the cat can get during the next n hours.

题意:总共有n个小时,在第i个小时你可以选择睡觉或吃饭,在第i小时睡觉将获得s[i]的愉悦值,吃饭将获得e[i]的愉悦值,你只能两者取其一,并且对于任意连续的k小时,必须睡觉ms小时,吃饭me小时,问能够获得的最大愉悦值是多少

思路:首先假设都在睡觉,则将获得 ∑ s [ i ] \sum{s[i]} s[i]的愉悦值,此时每次选择吃饭获得的愉悦值将变为 e [ i ] − s [ i ] e[i]-s[i] e[i]s[i],而对于任意区间 [ i , i + k ) [i,i+k) [i,i+k),因为至少要睡ms个小时,所以最多能吃k-ms小时,但至少应吃me个小时,如果说选取其中一个点就表示覆盖了 [ i , i + k ) [i,i+k) [i,i+k)这个区间,则区间覆盖次数需在[me,k-ms]之内

故问题转化为:共有n个点,对于其中每个点i,可以选取或不选,如果选取就会得到w[i] = e[i]-s[i]的愉悦值,但每个区间 [ i , i + k ) [i,i+k) [i,i+k)被覆盖的次数需要在 [ m e , k − m s ] [me,k-ms] [me,kms]之内,问最多能够获得多少愉悦值

建图技巧:源点S=0,汇点T=n+1,虚点n+2用于补流

1)对于所有点i,向i+k+1(如果大于T就置为T)连一条容量为1,权值为-w[i]的边

2)对于所有点i,向i+1连一条容量为k-ms-me,权值为0的边

3)从S向n+2连一条容量为k-ms的边,权值为0的边

4)从S向1 ~ k连一条容量为INF,权值为0的边

在流量最大的条件下跑最小费用即可

正确性:首先,步骤3保证了所有点的流量不会超过k-ms,而类似区间k覆盖问题,可以保证每个区间被覆盖的次数不超过k-ms;

其次,因为要保证最大流,故通过虚点向1 ~ k这些点补充了INF流量,在每个区间末端,即k ~ n都对应一个区间末端,都向下一个点有一条容量为k-ms-me的边,则对应区间都至少会被覆盖 (k-ms)-(k-ms-me) = me次,每次覆盖都会把对应权重加上

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define MAXN 2010
#define MAXM 3010
using namespace std;
const int INF = 0x3f3f3f3f;
int tot,head[MAXN];
struct edge
{
    int u,v,c,nxt;
    ll w;
}edg[MAXM<<1];
inline void addedg(int u,int v,int c,ll w)
{
    edg[tot].u = u;
    edg[tot].v = v;
    edg[tot].c = c;
    edg[tot].w = w;
    edg[tot].nxt = head[u];
    head[u] = tot++;
}
inline void add(int u,int v,int c,int w)
{
    addedg(u,v,c,w);
    addedg(v,u,0,-w);
}
int vis[MAXN],pre[MAXN],path[MAXN],N;
ll cost,d[MAXN];
//d用来存最短路,pre用来存路径,path用来存用了哪条边,cost用来保存最小费用
inline bool SPFA(int st,int ed)
{
    memset(d,0x3f,sizeof(ll)*(N+1));
    memset(pre,-1,sizeof(int)*(N+1));
    memset(path,-1,sizeof(int)*(N+1));
    memset(vis,0,sizeof(int)*(N+1));
    queue<int> qu;
    d[st] = 0;
    vis[st] = 1;
    qu.push(st);
    while(!qu.empty())
    {
        int u = qu.front();
        qu.pop();
        vis[u] = 0;
        for(int i = head[u];i != -1;i = edg[i].nxt)
        {
            int v = edg[i].v;
            if(d[u] + edg[i].w < d[v] && edg[i].c > 0)
            {
                d[v] = d[u] + edg[i].w;
                pre[v] = u;
                path[v] = i;
                if(!vis[v])
                {
                    vis[v] = 1;
                    qu.push(v);
                }
            }
        }
    }
    return pre[ed] != -1;
}
inline int minCostMaxFlow(int st,int ed)
{
    int flow = 0;
    while(SPFA(st,ed))
    {
        int minn = INF;
        for(int i = ed;i != st;i = pre[i])
            minn = min(minn,edg[path[i]].c);//求出新增加的流量
        for(int i = ed;i != st;i = pre[i])
        {
            edg[path[i]].c -= minn;
            edg[path[i]^1].c += minn;
        }
//        if(d[ed] >= 0) //保证费用最小,去掉后保证流量最大
//            break;
        flow += minn;
        cost += minn * d[ed];
    }
    return flow;
}
inline void init()
{
    tot = cost = 0;
    memset(head,-1,sizeof(int)*(N+1));
}
int n,k,ms,me,id[MAXN];
ll s[MAXN],e[MAXN];
int main()
{
    freopen("delight.in","r",stdin);
    freopen("delight.out","w",stdout);
    while(~scanf("%d%d%d%d",&n,&k,&ms,&me))
    {
        ll ans = 0;
        for(int i = 1;i <= n;++i)
        {
            scanf("%lld",&s[i]);
            ans += s[i];
        }
        int S = 0,T = n+1;
        N = T+1;
        init();
        for(int i = 1;i <= n;++i)
            scanf("%lld",&e[i]);
        for(int i = 1;i <= n;++i)
        {
            id[i] = tot;
            add(i,i+k>=T?T:i+k,1,-e[i]+s[i]);
            add(i,i+1,k-ms-me,0);
        }
        for(int i = 1;i <= k;++i)
            add(n+2,i,INF,0);
        add(S,n+2,k-ms,0);
        minCostMaxFlow(S,T);
        ans -= cost;
        printf("%lld\n",ans);
        for(int i = 1;i <= n;++i)
            if(edg[id[i]].c == 0)
                printf("E");
            else
                printf("S");
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值