冲刺NOI2017 (22) 养猫 (线性规划方程转网络流)

题目大意

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

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

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


题解

首先不考虑上述限制条件,假设猫的所有时刻都在吃东西,那么问题就转化为了从中选出一些时刻来睡觉,使其作息符合健康的要求,并使其愉悦值总和尽可能地高。

对于一个长度为 k 的区间,我们要选出的睡觉时刻总数一定要大于等于 min_sleep 且小于等于 kmin_sleep ,为了方便下面式子的书写,记 min_sleep mn ,记 kmin_sleep mx ,则对于任何一个长度为k的区间内被选出的睡觉时刻数量 num 都满足 mnnummx 。但是这种表达方式并不能列出线性规划方程,我们在下面会稍作变形。对于每一个时刻 i ,设 xi 表示时刻i是否被选为睡觉时刻,则有下列式子:

x11100000x21111000x31111110x41111110x50011110x60000110+a1b1+a2b2+a3b3=======mxmnmxmnmxmn0

上述式子的第1个第2个式子表示第一个长度为 k(k=4) 的区间,以此类推。式子中填入的1表示在这个式子中,此点是可选的。至于为什么在下面留了一行空公式,在后面的时候会解释。

上式为了满足线性规划方程的性质同时也易于处理,使用加或减一个不定取值的非负整数 ai bi ,代替式子中的不等号的使用。但是上面的式子仍然看不出什么性质,这时我们就需要将式子化简一下。

.

'''''''x11010000x21000100x31000001x41000001x50010001x60000101+a1a1b1+b1+a2a2b2+b2+a3a3b3+b3=======mxmnmxmxmnmnmxmxmnmnmxmn

这里简要描述一下化简式子的方式:式②’=式②-式①,式③’=式③-式②……以此类推。

化简之后,每一列 xi 都有且仅有一个1和一个-1,这样就可以转化为网络流了啊!(因为每一条边的流入流量等于其流出流量,此处只可意会不可言传)。这样的线性规划方程转化成网络流的具体方法为:先把每个式子当作一个结点来考虑。然后,对于等号左侧的式子的不为0的部分,值为正表示为一条出边,值为负则表示为一条入边。对于每一列 xi ,在值为1的式子和值为-1的式子之间连边;其余的部分在绝对值相等的两式上连边。对于等号右侧的式子,因为所有的值都是常量,所以流量都是确定的(都是常量的绝对值),所以值为正的部分从源点连到该公式,值为负的部分从该公式连到汇点。

这里给出第三个点(公式③’)的连边的例子,常量部分分别与S,T连边, ai,bi 由于都是取值是非负整数的变量,所以流量设为inf,表示可以在非负整数范围内随意取值,每选一条从 xi 列上产生的边就相当于把一个时刻从吃饭变成了睡觉,所以权值 vi 应为 sici ,又因为要求的是最大价值,所以取个相反数就可以咯。


这里写图片描述

总说线性规划转网络流是只可意会不可言传的?这里我稍微言传一下,因为这里面每一个时刻是否选用只能用一条边的形式表达出来,且一条边的流出量一定等于流入量,所以 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;
}
发布了104 篇原创文章 · 获赞 127 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_33330876/article/details/73392004
今日推荐