题目大意:
每天会有一个客人来图书馆,共n天,第i个客人要看的书是ai。
为了保证每个客人都能读到想要的书,你可以在任意时间去商店买书并放入图书馆或者从图书馆里丢掉几本书,图书馆里最多只能有k本书。
第i本书的价格是c[i]。问最少花费是多少?
用pre[i]表示在i之前第一次出现a[i]的位置,如果之前没有出现pre[i]=0。
假设k=1,那么只要将所有pre[i]!=i-1的c[a[i]]相加。
当k>1时,考虑用费用流求最多能减少的花费。
枚举i:
1、连一条从i连向i+1,容量为k-1,费用为0的边,表示当i点之前的书的数量小于k时可以买i。
2、若pre[i]>0且pre[i]!=i-1,新建一个点j,按如下方式连边(边容量都为1):
- 连一条从S到pre[i]+1,费用为0的边。
- 连一条从pre[i]+1到j,费用为0的边。
- 连一条从i到j,费用为-c[a[i]]的边。
连一条从j到T,费用为0的边。
然后求一遍最小费用最大流就是最多能减少的花费了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200
#define M 40000
#define INF 2000000000
struct Edge{
int t,w,f,fl,c,nx;
}e[M<<1];
int d[N],Ans,i,j,a[N],h[N],Num=1,k,n,m,p[N],pre[N],L[N],c[N],s,t,Cnt,x,q[M],l,r;
bool b[N];
inline int Min(int x,int y){return x<y?x:y;}
inline void Add(int x,int y,int w,int c){
e[++Num].t=y;e[Num].w=w;e[Num].c=c;e[Num].f=x;e[Num].nx=h[x];h[x]=Num;
e[++Num].t=x;e[Num].w=-w;e[Num].f=y;e[Num].nx=h[y];h[y]=Num;
}
inline int Spfa(){
memset(d,63,sizeof(d));
memset(b,0,sizeof(b));
memset(p,0,sizeof(p));
d[s]=l=0;b[s]=1;q[r=1]=s;
while(++l<=r){
x=q[l];
b[x]=0;
for(i=h[x];i;i=e[i].nx)
if(e[i].c>e[i].fl&&d[e[i].t]>d[x]+e[i].w){
d[e[i].t]=d[x]+e[i].w;
p[e[i].t]=i;
if(!b[e[i].t])q[++r]=e[i].t,b[e[i].t]=1;
}
}
if(d[t]==d[0])return d[0];
for(i=p[t];i;i=p[e[i].f])e[i].fl++,e[i^1].fl--;
return d[t];
}
int main(){
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)scanf("%d",&c[i]);
for(i=1;i<=n;i++){
pre[i]=L[a[i]];L[a[i]]=i;
if(i==1||pre[i]!=i-1)Ans+=c[a[i]];
}
for(i=1;i<=n;i++)Add(i,i+1,0,k-1);
Cnt=n+1;
s=++Cnt;
for(i=1;i<=n;i++)
if(pre[i]&&pre[i]!=i-1){
Add(s,pre[i]+1,0,1);
Add(pre[i]+1,++Cnt,0,1);
Add(i,Cnt,-c[a[i]],1);
}
t=++Cnt;
for(i=s+1;i<t;i++)Add(i,t,0,1);
while(1){
x=Spfa();
if(x==d[0])break;
Ans+=x;
}
printf("%d\n",Ans);
return 0;
}

294

被折叠的 条评论
为什么被折叠?



