gym101190D. Delight for a Cat(费用流建模好题,每k个至少选...)

题目链接 gym101190D

Problem D. Delight for a Cat
Input file: delight.in Output file: delight.out
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. Input
Thefirstlineoftheinputcontainsfourintegersn,k,ms,andme (1≤k≤n≤1000;0≤ms,me ≤k; ms + me ≤ k) — the number of upcoming hours, the length of the period (in hours), and the minimum number of hours the cat should be sleeping and eating out of every k consecutive hours, respectively.
The second line contains n integers s1,s2,...,sn (0 ≤ si ≤ 109) — the amount of delight the cat gets when it is sleeping during the first, the second, ..., the n-th hour.
The third line contains n integers e1, e2, . . . , en (0 ≤ ei ≤ 109) — the amount of delight the cat gets when it is eating during the first, the second, ..., the n-th hour.
Output
In the first line, output a single integer — the maximum total amount of delight the cat can get during the next n hours.
In the second line, output a string of length n consisting of characters “S” and “E”. The i-th character of this string should correspond to whether the cat should sleep (“S”) or eat (“E”) in the i-th hour to get the maximum total amount of delight out of these n hours.
Example

delight.in
delight.out
10 4 1 2
1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
69
EEESESEESS

题意:给出 \(n\) 个小时,一只猫可以在每个小时睡觉或吃饭,猫在每个小时吃饭或者睡觉有个\(happy\)\(e[i]\)/\(s[i]\),同时给出 \(k\) ,要求每 \(k\) 个小时至少有 \(ms\) 的时间睡觉, \(me\) 的时间吃饭。问如何安排可以使得猫的 \(happy\) 值最大,并输出方案。

题解:假设刚开始时让猫一直睡觉, 那么此时可以得到 \(res=\sum s[i]\)\(happy\) 值,此时问题转化为在 \(n\) 个小时中,每 \(k\) 个小时最少选 \(min_e=me\),最多选 \(max_e=k-ms\) 个小时让他吃饭,同时每选一个可以获得 \(x[i]=e[i]-s[i]\) 的额外值。那么这么建图:

  1. \(i\)\(i+1\)(当\(i+1>n\) 时连 \(T\)),费用为0,容量为 \(max_e-min_e\).
  2. \(i\)\(i+k\)(当 \(i+k>n\) 时连\(T\)),费用为 \(-x[i]\),容量为1.
  3. \(S\)\(1,2,…,k\) 连费用为0,容量为 \(inf\) .
  4. \(S\)\(T\)流量为 \(max_e\) 的最小费用流(新增一个源点 \(S_0\) ,连\(S_0\)\(S\) 的流量为 \(max_e\) ,费用为0的边,求 \(S_0\)\(T\) 的最小费用最大流即可)。若最小费用为 \(ans\) ,那么\(-ans+res\)即为答案。同时,要求输出方案时对于每条第 \(2\) 类型的边,当满流时即为选\(i\) 吃饭。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll inf=1e17;
const ll mod=1000000007;

const int maxn=1000+10;
const int maxm=maxn*maxn*2; //

const int INF=1e9;
struct Edge
{
    int from,to,next,cap,flow,cost;
}edge[maxm];
int head[maxn],tol;
int pre[maxn];
ll dis[maxn];
bool vis[maxn];
int N;//节点总个数,节点编号从0~N-1
void init(int n)
{
    N=n;
    tol=0;
    for(int i=0;i<N;i++) head[i]=-1;
}
void addedge(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;
    for(int i=0;i<N;i++)
    {
        dis[i]=inf;
        vis[i]=false;
        pre[i]=-1;
    }
    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]+1ll*edge[i].cost)
            {
                dis[v]=dis[u]+1ll*edge[i].cost;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1)
        return false;
    else
        return true;
}
//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s,int t,ll &cost)
{
    int flow=0;
    cost=0;
    while(spfa(s,t))
    {
        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+=1ll*edge[i].cost*Min;
        }
        flow+=Min;
    }
    return flow;
}

int s[maxn];
char ans[maxn];
int main()
{
    freopen("delight.in","r",stdin);
    freopen("delight.out","w",stdout);
    int n,k,ms,me;
    scanf("%d%d%d%d",&n,&k,&ms,&me);
    ll sum=0;
    rep(i,1,n+1) scanf("%d",&s[i]),sum+=1ll*s[i],ans[i]='S';
    rep(i,1,n+1)
    {
        int v;
        scanf("%d",&v);
        s[i]=v-s[i];
    }
    int st=0,ed=n+1,sst=n+2;
    init(ed+2);
    rep(i,1,n+1)
    {
        if(i+1>n) addedge(i,ed,k-ms-me,0);
        else addedge(i,i+1,k-ms-me,0);
        if(i+k>n) addedge(i,ed,1,-s[i]);
        else addedge(i,i+k,1,-s[i]);
    }
    rep(i,1,k+1) addedge(st,i,1e8,0);
    addedge(sst,st,k-ms,0);
    ll res=0;
    minCostMaxflow(sst,ed,res);
    sum+=-res;
    for(int u=1;u<=n;u++)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].flow==edge[i].cap&&v==min(u+k,ed)&&edge[i].cost==-s[u])
                ans[u]='E';
        }
    }
    ans[n+1]='\0';
    printf("%lld\n",sum);
    printf("%s\n",ans+1);
    return 0;
}
posted @ 2017-09-08 09:51  tarjan's  阅读(182)  评论(0编辑  收藏  举报