[CF802C]Heidi and Library (hard)

题意

你有一个容量为\(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 ;
}
posted @ 2019-04-17 21:06  beretty  阅读(138)  评论(0编辑  收藏  举报