CF802C Heidi and Library (hard)

题目描述

你有一个容量为k的空书架,现在共有n个请求,每个请求给定一本书ai,如果你的书架里没有这本书,你就必须以ci的价格购买这本书放入书架。当然,你可以在任何时候丢掉书架里的某本书。请求出完成这n个请求所需要的最少价钱。

题解

对于每个请求,我们可以强制让他必须买,然后再去考虑如何扣掉之前的贡献。

对于每一次购买,我们可以作如下两种策划。

1、刚买完就扔掉。2、在下一次购买之前把这本书卖掉(这里的卖掉相当于是把上一次买的代价减去了)。

按照这样的策略,我们的每一本买来的书最终都会被弄掉 。

这样的话,我们给每一次购买开一个垃圾桶,表示这本书经过若干轮之后必须回到垃圾桶里。

设源点为S,汇点为T。

从源点向每次询问连边权为1,费用为w的边,表示每次询问一定要买。

每次询问向每次的垃圾桶连边权为1,费用为0,表示我可以刚买完就扔掉。

每次询问向下一个询问连边权为k-1,费用为0的边,表示在下一次购买前我最多可以有k-1本书。

对于一次询问,如果下一次询问的书之前出现过,则一条从当前询问到那一次的垃圾桶连一条边权为1,费用-w的边,代表把书卖掉的情况。

费用流即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 170
#define inf 1e9
using namespace std;
queue<int>q;
int head[N],tot=1,ans,fl[N],dis[N],pre[N],n,k,a[N],c[N],now[N];
bool vis[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x; 
}
struct edge{
    int n,to,l,f;
}e[N*N];
inline void add(int u,int v,int l,int f){
    e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[tot].f=f;
    e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;e[tot].f=-f;
}
inline bool spfa(int s,int t){
    memset(dis,0x3f,sizeof(dis));
    q.push(s);dis[s]=0;fl[s]=inf;
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].n){
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].f&&e[i].l){
                dis[v]=dis[u]+e[i].f;//cout<<u<<" "<<v<<" "<<dis[u]<<" "<<dis[v]<<endl;
                pre[v]=i;fl[v]=min(fl[u],e[i].l);
                if(!vis[v]){vis[v]=1;q.push(v);}
            }
        }
    }
    return dis[t]!=0x3f3f3f3f;
}
void calc(int s,int t){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].l-=fl[t];e[i^1].l+=fl[t];
        x=e[i^1].to; 
    }
    ans+=fl[t]*dis[t];
}
int main(){
    n=rd();k=rd();
    for(int i=1;i<=n;++i)a[i]=rd();
    for(int i=1;i<=n;++i)c[i]=rd();
    for(int i=1;i<=n;++i){
        if(i!=n)add(i,i+1,k-1,0);
        add(0,i,1,c[a[i]]);
        add(i,i+n,1,0);add(i+n,2*n+1,1,0);now[a[i]]=i;
        if(now[a[i+1]]){
            add(i,now[a[i+1]]+n,1,-c[a[i+1]]);
        }
    }
    while(spfa(0,2*n+1))calc(0,2*n+1);
    cout<<ans; 
    return 0;
}
View Code
posted @ 2019-03-09 20:02  comld  阅读(212)  评论(0编辑  收藏  举报