题意
你有一个容量为\(k\)的书架,每次给定一本书,如果集合中没有这个书就需要花费\(c_i\)的价格将这个书加入集合,可以随时丢掉集合内的书,求完成所有请求的最小花费
题解
为什么我一开始看到这题会想到最小割==
但是每种物品是可以多次对答案造成贡献的,所以显然不能最小割,只能费用流了
那么考虑怎么建图
考虑买入有点困难
可以考虑卖出,卖出的意义就是买入了一个原先有的东西
那么我们把每个要求拆点
拆成两个点\(D_1,D_2\)
然后\(S\to D1\)连流量为1,费用为这天物品的花费的边
\(D_1\to D_2\)连流量为1,费用为0的边表示扔掉这本书
\(D2\to T\)连流量为1,费用为0的边表示今天的物品是否扔掉
\(D_{i,1} \to D_{i+1,1}\)连流量为\(k-1\),费用为0的边表示最多保留\(k-1\)本书,因为明天还要买入一本
\(D_{i-1,1} \to D_{pre_i,2}\)表示已经有这本书了,可以把第\(i\)天出现的这本书卖掉了(\(pre_i\)表示第\(i\)天出现的书上一个出现的时间)
然后最小费用流即可
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 205 ;
const int INF = 1e9 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
bool exist[M] ;
int n , k , S , T , num = 1 , hea[M] ;
int idx[M] , cst[M] , prep[M] , pos[M] ;
int dis[M] , pre[M] , ans ;
struct E {
int nxt , to , dis , cst ;
} edge[M * 20] ;
inline void Insert_edge(int from , int to , int dis , int cst) {
edge[++num].nxt = hea[from] ; edge[num].to = to ;
edge[num].dis = dis ; edge[num].cst = cst ; hea[from] = num ;
}
inline void add_edge(int u , int v , int w , int c) {
Insert_edge(u , v , w , c) ;
Insert_edge(v , u , 0 , -c) ;
}
inline bool Spfa() {
queue < int > q ; q.push(S) ; pre[T] = -1 ;
memset(dis , 63 , sizeof(dis)) ; dis[S] = 0 ;
while(!q.empty()) {
int u = q.front() ; q.pop() ; exist[u] = false ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(dis[v] > dis[u] + edge[i].cst && edge[i].dis > 0) {
pre[v] = i ;
dis[v] = dis[u] + edge[i].cst ;
if(!exist[v]) {
exist[v] = true ;
q.push(v) ;
}
}
}
}
return (pre[T] > 0) ;
}
inline void Mcmf() {
while(Spfa()) {
int diss = INF ;
for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to)
diss = min(diss , edge[pre[i]].dis) ;
for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to)
edge[pre[i]].dis -= diss , edge[pre[i] ^ 1].dis += diss ;
ans += dis[T] * diss ;
}
}
int main() {
n = read() ; k = read() ; S = 0 ; T = n * 2 + 1 ;
for(int i = 1 ; i <= n ; i ++) {
idx[i] = read() ;
prep[i] = pos[idx[i]] ;
pos[idx[i]] = i ;
}
for(int i = 1 ; i <= n ; i ++) cst[i] = read() ;
for(int i = 1 ; i <= n ; i ++) {
add_edge(S , i * 2 - 1 , 1 , cst[idx[i]]) ;
add_edge(i * 2 - 1 , i * 2 , 1 , 0) ;
add_edge(i * 2 , T , 1 , 0) ;
if(i != n) add_edge(i * 2 - 1 , (i + 1) * 2 - 1 , k - 1 , 0) ;
if(prep[i]) add_edge((i - 1) * 2 - 1 , prep[i] * 2 , 1 , -cst[idx[i]]) ;
}
Mcmf() ;
printf("%d\n",ans) ;
return 0 ;
}