BZOJ.4842.[NEERC2016]Delight for a Cat(费用流)

BZOJ


参考这儿

首先如果一个活动的时间满足条件,那么另一个活动也一定满足。还有就是这题就是费用流没有为什么。不妨假设最初所有时间都用来睡觉,那么我们要对每个\(k\)大小区间选出\([t2,k-t1]\)个时刻打游戏,同时要让选出的\(\sum s_i-e_i\)尽量小。

假如要求打游戏的时间恰好为\(t2\)怎么做呢?
对每个时刻建一个点\(i\),由\(i\)\(i+k\)连边(不存在则直接连向汇点),容量\(1\)费用\(s_i-e_i\)。建一个虚点\(p\)\(p\)\(1\sim k\)每个点连容量\(1\)费用\(0\)的边,源点\(S\)\(p\)连容量\(t2\)费用\(0\)的边。
这样如果选了\(i\)就一定会选\(i+k\),所以每个\(k\)区间都会恰好选了\(t2\)个时刻。

那现在打游戏的时间可以提高到\(k-t1\)呢。
\(i\)\(i+1\)连边,容量\(k-t1-t2\)费用\(0\)\(S\)\(p\)的连边容量改成\(k-t1\)
这样对于每个\(k\)区间,应该有的\(t2\)条打游戏的边和之前一样,不会少走。还可以多选\(k-t1-t2\)个时刻打游戏。

线性规划的做法实在是不想看惹...出了也做不出来


//980kb 4212ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1007,M=3*N*2;
const LL INF=0x3f3f3f3f3f3f3f3f;

int S,T,A[N],B[N],Enum,H[N],nxt[M],to[M],cap[M],len[M],cur[N];
bool vis[N];
LL Cost,dis[N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline void AE(int u,int v,int w,int c)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, cap[Enum]=w, len[Enum]=c;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, cap[Enum]=0, len[Enum]=-c;
}
bool SPFA()
{
    static bool inq[N];
    static std::queue<int> q;
    memset(dis,0x3f,T+1<<3);//<<3!!!
    dis[S]=0, q.push(S);
    while(!q.empty())
    {
        int x=q.front();
        q.pop(), inq[x]=0;
        for(int i=H[x],v; i; i=nxt[i])
            if(cap[i]&&dis[to[i]]>dis[x]+len[i])
                dis[v=to[i]]=dis[x]+len[i], !inq[v]&&(q.push(v),inq[v]=1);
    }
    return dis[T]<INF;
}
bool DFS(int x)
{
    if(x==T) return 1;
    vis[x]=1;
    for(int &i=cur[x],v; i; i=nxt[i])
        if(!vis[v=to[i]]&&dis[v]==dis[x]+len[i]&&cap[i]&&DFS(v))//&&cap[i]!!! 有必要的啊 
            return --cap[i],++cap[i^1],Cost+=len[i],1;
    return 0;
}
void MCMF()
{
    while(SPFA())
    {
        memcpy(cur,H,T+1<<2), memset(vis,0,T+1);
        while(DFS(S));
    }
}

int main()
{
    static int e[N];
    int n=read(),K=read(),t1=read(),t2=read(),P=n+1;
    Enum=1, S=0, T=n+2; LL ans=0;
    for(int i=1; i<=n; ++i) ans+=A[i]=read();
    for(int i=1; i<=n; ++i) B[i]=read();
    AE(S,P,K-t1,0);
    for(int i=1; i<=K; ++i) AE(P,i,1,0);
    for(int i=1,tmp=K-t1-t2; i<=n; ++i) AE(i,i+1>n?T:i+1,tmp,0), AE(i,i+K>n?T:i+K,1,A[i]-B[i]), e[i]=Enum;
    MCMF(), printf("%lld\n",ans-Cost);
    for(int i=1; i<=n; ++i) putchar(cap[e[i]]?'E':'S');

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10725184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值