C. Heidi and Library

题意

有 n 种分别具有价格 b 的书 a ,图书馆里最多同时存放 k 本书,已知接下来 n 天每天都有一个人来看某一本书,如果图书馆里没有则需要购买,问最少花费多少钱。

分析

这道题的一个简单版本 ,默认所有书价格为1,那么只需要用 set 维护一下,当图书馆内库存满的时候 erase 掉距离最大的书(贪心做法)。

但是有了价格之后,就不能贪心了,然后这道题就可以用神奇的网络流解决。准确的说是最小费用最大流。

所有边的容量为 1,将 n 个点拆成 2n 个点(2i,2i + 1),2i 和 2i + 1 连边,花费 -INF(因为求的最小费用,这里保证了 n 个点都会经过到),源点 S 连边 2i,费用为 b[a[i]] ,2i + 1 连边汇点 T,费用为 0 。遍历 n 个点,2i + 1 连边 2j ,如果 a[j] == a[i] ,费用为 0,否则费用为 b[a[j]]。

以上就是建图的过程,我们可以把 k 当作网络流的流量,以样例1为例。

4 80 // n k
1 2 2 1 // 看的书的种类
1 1 1 1 // 价格

当 k = 1 时,



首先最小费用流会保证一次跑完所有费用为 -INF 的边,保证了经过所有点,那么 k = 1 其实就是除非连续的几天种类相同,不然都要扔掉,买另一种书,我们在加边的时候已经处理了,比如相邻的边 (3 -> 4) 费用为 0。这种情况下花费为 3。



当 k = 2 时,引入新边 (S -> 2),它会走边(2 -> 1),这个边其实就是网络流提供给算法一种 “反悔” 的策略,如蓝色轨迹所示,其中(1 -> 2,2 -> 1)(5 -> 6,6 -> 5)两对边,全部抵消掉了。



那么其实在 k = 2 时,真正的策略如上图轨迹所示,我们可以将 k 当作 k 个放书的位置,每个位置互不影响,其中第一个位置存放 1 这本书,在第 0 天和第 3 天分别被借阅,第二个位置存放 2 这本书,在第 1 天和第 2 天分别被借阅 。

首先用 -INF 保证所有点都被按顺序访问到(第一次搜索时),在根据 k 的值,不断的做出 “反悔” 策略,最终达到最小费用。

注意:当判断到 dis[T] >= 0 时就要退出了,这个时候多加的 k 也毫无意义了。最后答案加上 n * INF。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10; //
const int MAXM = 2e5 + 5; // 注意扩张的边的数量
const ll INF = 1e9;
struct Edge {
int to, next, cap, flow, cost;
} edge[MAXM * 4];
int head[MAXN], tol;
int pre[MAXN];
ll dis[MAXN];
bool vis[MAXN];
int N;
void init(int n) {
N = n;
tol = 0;
memset(head, -1, sizeof head);
memset(pre, 0, sizeof pre);
memset(dis, 0, sizeof dis);
memset(vis, 0, sizeof vis);
memset(edge, 0, sizeof edge);
}
void addedge (int u, int v, int cap, int cost) {
edge[tol] = Edge{v, head[u], cap, 0, cost};
head[u] = tol++;
edge[tol] = Edge{u, head[v], 0, 0, -cost};
head[v] = tol++;
}
bool spfa(int s, int t) {
queue<int>q;
for(int i = 0; i < N; i++) {
dis[i] = INF * INF;
vis[i] = false;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i]. to;
if(edge[i].cap > edge[i].flow &&
dis[v] > dis[u] + edge[i].cost) {
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return dis[t] < 0;
}
int a[MAXN], b[MAXN];
int main() {
int n, k;
cin >> n >> k;
for(int i = 0; i < n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n; i++) {
cin >> b[i];
}
init(MAXN);
int s = 2 * n, t = 2 * n + 1;
for(int i = 0; i < n; i++) {
addedge(s, 2 * i, 1, b[a[i]]);
addedge(2 * i + 1, t, 1, 0);
addedge(2 * i, 2 * i + 1, 1, -INF);
}
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
addedge(2 * i + 1, 2 * j, 1, a[i] == a[j] ? 0 : b[a[j]]);
}
}
ll cost = 0;
for(int i = 0; i < k; i++) {
if(!spfa(s, t)) break;
for(int j = pre[t]; j != -1; j = pre[edge[j^1].to]) {
edge[j].flow++;
edge[j^1].flow--;
}
cost += dis[t];
}
cout << cost + 1LL * n * INF<< endl;
return 0;
}

C. Heidi and Library (神奇的网络流)的更多相关文章

  1. 【CF802C】Heidi and Library(网络流)

    [CF802C]Heidi and Library(网络流) 题面 CF 洛谷 题解 前面两个Easy和Medium都是什么鬼玩意啊.... 不难发现如果这天的要求就是第\(a_i\)种书的话,那么\ ...

  2. 题解-CF802C Heidi and Library (hard)

    题面 CF802C Heidi and Library (hard) 有一个大小为 \(k\) 的空书架.有 \(n\) 天和 \(n\) 种书,每天要求书架中有书 \(a_i\).每天可以多次买书, ...

  3. CF802C Heidi and Library hard 费用流 区间k覆盖问题

    LINK:Heidi and Library 先说一下简单版本的 就是权值都为1. 一直无脑加书 然后发现会引起冲突,可以发现此时需要扔掉一本书. 扔掉的话 可以考虑扔掉哪一本是最优的 可以发现扔掉n ...

  4. 【CF802C】Heidi and Library (hard) 费用流

    [CF802C]Heidi and Library (hard) 题意:有n个人依次来借书,第i人来的时候要求书店里必须有种类为ai的书,种类为i的书要花费ci块钱购入.而书店的容量只有k,多余的书只 ...

  5. 贪心算法 Heidi and Library (easy)

    A. Heidi and Library (easy) time limit per test 2 seconds memory limit per test 256 megabytes input ...

  6. 【网络流】【待补】C. Heidi and Library (hard)

    http://codeforces.com/contest/802/problem/C

  7. 【网络流】codeforces C. Heidi and Library (hard)

    http://codeforces.com/contest/802/problem/C

  8. 洛谷P3159 交换棋子 神奇的网络流

    神奇的建模...原题链接 如果你真的把交换看成交换,就\(GG\)了.首先我们要把交换看成是白棋的移动. 然后,很容易的就想到建模的大致思路:建立超级源点S和超级汇点T,从S向初始局面每个白棋所在的格 ...

  9. CF.802C.Heidi and Library (hard) (费用流zkw)

    题目链接 复习了下餐巾计划问题.全忘了=-= 首先这是一道网络流.然后本题有\(n\)种建图方法,以及\(smy\) dalao还有单纯形做法. 先假设所有物品都是买入的.那么对于每一天,拆成两个点\ ...

随机推荐

  1. 【Pascal's Triangle】cpp

    题目: Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,R ...

  2. JMeter学习笔记(三) 录制脚本

    jmeter测试脚本,可以通过其他工具进行录制,例如 BadBoy,我之前使用过此工具,安装以及使用都比较简单的,大家可以在网上搜索一下. 在此整理一下jmeter自带的录制功能,进行录制脚本. 1. ...

  3. 【Python-遇到的Error】AttributeError: 'str' object has no attribute 'input_text'

    学习类的实例化的时候遇到了AttributeError: 'str' object has no attribute 'input_text', 以下是报错的代码及修改正确的代码. class shu ...

  4. Canvas 图形组合方式

    /** * 图形组合 */ function initDemo5() { var canvas = document.getElementById("demo5"); if (!c ...

  5. (原)UE4.20 自定义编辑器 - 基础(一)创建编辑器模块

            @author:白袍小道 前言: 本小文参考了UnrealC++,游戏编辑器(应该都找不到了嘿嘿)等书籍. 引擎基于UnrealEngine4.20版本(由于UnrealC++ 用的是 ...

  6. Leetcode 665.非递减数列

    非递减数列 给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i ...

  7. java安全提交笔记【xmind图片】

  8. nio的reactor模式

    转自:http://blog.csdn.net/it_man/article/details/38417761 线程状态转换图 就是非阻塞IO 采用多路分发方式举个例子吧,你服务器做一个聊天室,按照以 ...

  9. [洛谷P4074][WC2013]糖果公园

    题目大意:给一棵$n$个节点的树,每个点有一个值$C_i$,每次询问一条路径$x->y$,求$\sum\limits_{c}val_c\times \sum\limits_{i=1}^{cnt_ ...

  10. BZOJ 1036 [ZJOI2008]树的统计Count | 树链剖分模板

    原题链接 树链剖分的模板题:在点带权树树上维护路径和,最大值和单点修改 这里给出几个定义 以任意点为根,然后记 size (u ) 为以 u 为根的子树的结点个数,令 v 为 u 所有儿子中 size ...