题目大意
你养了一只猫,为了让它快乐地成长,你需要合理地安排它每天的作息时间。假设一天分为
n
个时刻,猫在每个时刻要么是吃东西,要么是睡觉。在第
猫要成长,不仅仅需要快乐,还需要健康的作息。经过研究,对于每一个连续的长度为
k
的作息时间,即所有的时刻区间
现在你想合理地安排一天中的这n个时刻,使得猫在能健康成长的前提下,获得尽量多的愉悦值。
题解
首先不考虑上述限制条件,假设猫的所有时刻都在吃东西,那么问题就转化为了从中选出一些时刻来睡觉,使其作息符合健康的要求,并使其愉悦值总和尽可能地高。
对于一个长度为
k
的区间,我们要选出的睡觉时刻总数一定要大于等于
上述式子的第1个第2个式子表示第一个长度为 k(k=4) 的区间,以此类推。式子中填入的1表示在这个式子中,此点是可选的。至于为什么在下面留了一行空公式,在后面的时候会解释。
上式为了满足线性规划方程的性质同时也易于处理,使用加或减一个不定取值的非负整数 ai 和 bi ,代替式子中的不等号的使用。但是上面的式子仍然看不出什么性质,这时我们就需要将式子化简一下。
.
这里简要描述一下化简式子的方式:式②’=式②-式①,式③’=式③-式②……以此类推。
化简之后,每一列 xi 都有且仅有一个1和一个-1,这样就可以转化为网络流了啊!(因为每一条边的流入流量等于其流出流量,此处只可意会不可言传)。这样的线性规划方程转化成网络流的具体方法为:先把每个式子当作一个结点来考虑。然后,对于等号左侧的式子的不为0的部分,值为正表示为一条出边,值为负则表示为一条入边。对于每一列 xi ,在值为1的式子和值为-1的式子之间连边;其余的部分在绝对值相等的两式上连边。对于等号右侧的式子,因为所有的值都是常量,所以流量都是确定的(都是常量的绝对值),所以值为正的部分从源点连到该公式,值为负的部分从该公式连到汇点。
这里给出第三个点(公式③’)的连边的例子,常量部分分别与S,T连边,
ai,bi
由于都是取值是非负整数的变量,所以流量设为inf,表示可以在非负整数范围内随意取值,每选一条从
xi
列上产生的边就相当于把一个时刻从吃饭变成了睡觉,所以权值
vi
应为
si−ci
,又因为要求的是最大价值,所以取个相反数就可以咯。
总说线性规划转网络流是只可意会不可言传的?这里我稍微言传一下,因为这里面每一个时刻是否选用只能用一条边的形式表达出来,且一条边的流出量一定等于流入量,所以 xi 列只能化简到最后为有且仅有一个1个一个-1的形式。把每个等式作为结点的原因也很简单,就是利用了网络流中每个结点的流出量等于流入量这个性质,粗暴一点来说等式左侧的就是流出口,等式右侧的就是流入口,而也因为值有正有负,所以负值部分的式子就表示为“流入流出口”或“流出流入口”这样的形式了。
代码
#include <cstdio>
#include <iostream>
//#include <ctime>
#include <queue>
using namespace std;
const int maxn=int(2e3)+111;
int n,k,minc,mins;
int c[maxn], s[maxn];
void Read() {
scanf("%d%d%d%d",&n,&k,&minc,&mins);
register int i;
for(i=1;i<=n;++i) scanf("%d",&c[i]);
for(i=1;i<=n;++i) scanf("%d",&s[i]);
return;
}
const int maxe=int(2e6)+111, inf=int(1e9)+7;
const long long INF=1ll<<60;
int S,T;
int id[maxn];
int head[maxn], tot=0;
struct Edge {
int to,cap,cost,next;
Edge() {}
Edge(int y,int ca,int co,int nx):to(y),cap(ca),cost(co),next(nx) {}
}eage[maxe<<1];
inline void Add_eage(int x,int y,int cap,int cost) {
eage[tot]=Edge(y,cap,cost,head[x]), head[x]=tot++;
eage[tot]=Edge(x,0,-cost,head[y]), head[y]=tot++;
}
void Build() {
int mx=k-minc,mn=mins,num=(n-k+1)<<1;
S=0, T=num+2;
register int i;
for(i=S;i<=T;++i)
head[i]=-1;
//Constant on rhs
Add_eage(S,1,mx,0);
Add_eage(num+1,T,mn,0);
for(i=2;i<=num;++i) {
if(i&1) Add_eage(S,i,mx,0), Add_eage(i,T,mn,0);
else Add_eage(S,i,mn,0), Add_eage(i,T,mx,0);
}
//Variable on lhs
for(i=1;i<=num+1;i+=2) {
if(i<=num) Add_eage(i,i+1,inf,0);
if(i>1) Add_eage(i,i-1,inf,0);
}
//Keypoint on lhs
for(i=1;i<=n;++i) {
if(1+(i<<1)>num) Add_eage(i>k?1+((i-k)<<1):1,num+1,1,c[i]-s[i]);
else Add_eage(i>k?1+((i-k)<<1):1,1+(i<<1),1,c[i]-s[i]);
id[i]=tot-1;
}
}
long long dis[maxn];
bool used[maxn], vis[maxn];
queue<int> que;
bool spfa() {
while(que.size()) que.pop();
register int i,u,v;
for(i=0;i<maxn;++i) {
dis[i]=INF;
used[i]=false, vis[i]=false;
}
que.push(S);
used[S]=true;
dis[S]=0;
while(que.size()) {
u=que.front(); que.pop();
used[u]=false;
for(i=head[u];~i;i=eage[i].next) if(eage[i].cap && dis[eage[i].to]>dis[u]+eage[i].cost) {
v=eage[i].to;
dis[v]=dis[u]+eage[i].cost;
if(!used[v]) {used[v]=true; que.push(v);}
}
}
return dis[T]<INF;
}
long long ans=0;
int dfs(int u,int flow) {
if(u==T || !flow) {
ans+=dis[u]*flow;
return flow;
}
vis[u]=true;
int ret=0;
register int i,v;
for(i=head[u];~i;i=eage[i].next) if(eage[i].cap && !vis[eage[i].to] && dis[eage[i].to]==dis[u]+eage[i].cost) {
v=eage[i].to;
int newf=dfs(v,min(flow,eage[i].cap));
eage[i].cap-=newf;
eage[i^1].cap+=newf;
ret+=newf;
flow-=newf;
if(!flow) break;
}
if(!ret) dis[u]=-1;
return ret;
}
int MCMF() {
int res=0; ans=0;
while(spfa()) res+=dfs(S,inf);
ans=-ans;
return res;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("cat.in","r",stdin);
freopen("cat.out","w",stdout);
#endif
// double t1=clock();
Read();
Build();
MCMF();
register int i;
for(i=1;i<=n;++i) ans+=c[i];
cout<<ans<<endl;
for(i=1;i<=n;++i) {
if(eage[id[i]].cap) putchar('E');
else putchar('S');
}
putchar('\n');
// printf("%.3lfsec\n",(clock()-t1)/CLOCKS_PER_SEC);
return 0;
}