C. Heidi and Library (神奇的网络流)
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 (神奇的网络流)的更多相关文章
- 【CF802C】Heidi and Library(网络流)
[CF802C]Heidi and Library(网络流) 题面 CF 洛谷 题解 前面两个Easy和Medium都是什么鬼玩意啊.... 不难发现如果这天的要求就是第\(a_i\)种书的话,那么\ ...
- 题解-CF802C Heidi and Library (hard)
题面 CF802C Heidi and Library (hard) 有一个大小为 \(k\) 的空书架.有 \(n\) 天和 \(n\) 种书,每天要求书架中有书 \(a_i\).每天可以多次买书, ...
- CF802C Heidi and Library hard 费用流 区间k覆盖问题
LINK:Heidi and Library 先说一下简单版本的 就是权值都为1. 一直无脑加书 然后发现会引起冲突,可以发现此时需要扔掉一本书. 扔掉的话 可以考虑扔掉哪一本是最优的 可以发现扔掉n ...
- 【CF802C】Heidi and Library (hard) 费用流
[CF802C]Heidi and Library (hard) 题意:有n个人依次来借书,第i人来的时候要求书店里必须有种类为ai的书,种类为i的书要花费ci块钱购入.而书店的容量只有k,多余的书只 ...
- 贪心算法 Heidi and Library (easy)
A. Heidi and Library (easy) time limit per test 2 seconds memory limit per test 256 megabytes input ...
- 【网络流】【待补】C. Heidi and Library (hard)
http://codeforces.com/contest/802/problem/C
- 【网络流】codeforces C. Heidi and Library (hard)
http://codeforces.com/contest/802/problem/C
- 洛谷P3159 交换棋子 神奇的网络流
神奇的建模...原题链接 如果你真的把交换看成交换,就\(GG\)了.首先我们要把交换看成是白棋的移动. 然后,很容易的就想到建模的大致思路:建立超级源点S和超级汇点T,从S向初始局面每个白棋所在的格 ...
- CF.802C.Heidi and Library (hard) (费用流zkw)
题目链接 复习了下餐巾计划问题.全忘了=-= 首先这是一道网络流.然后本题有\(n\)种建图方法,以及\(smy\) dalao还有单纯形做法. 先假设所有物品都是买入的.那么对于每一天,拆成两个点\ ...
随机推荐
- jsonp、瀑布流布局、组合搜索、多级评论(评论树)、Tornado初识
1.JSONP原理剖析以及实现 1.1 同源策略限制 用django分别建立两个项目,jsonp01和jsonp02,然后再在这两个项目里分别建立一个app,比如名字叫jsonp1.jsonp2:js ...
- Leetcode 659.分割数组为连续子序列
分割数组为连续子序列 输入一个按升序排序的整数数组(可能包含重复数字),你需要将它们分割成几个子序列,其中每个子序列至少包含三个连续整数.返回你是否能做出这样的分割? 示例 1: 输入: [1,2,3 ...
- 团队项目-第四次Scrum 会议
时间:10.26 时长:30分钟 地点:线上 工作情况 团队成员 已完成任务 待完成任务 解小锐 编写project和projectGenerator类 编写下一步的规格说明 陈鑫 采用creator ...
- Android记事本11
昨天: Activity的启动模式. 今天: 分析了一些网上的例子的源码. 遇到问题: 无.
- 【DNS】- 域名解析中A记录、CNAME、MX记录、NS记录的区别和联系
1. A记录 又称IP指向,用户可以在此设置子域名并指向到自己的目标主机地址上,从而实现通过域名找到服务器.说明:·指向的目标主机地址类型只能使用IP地址: 附加说明: 1) 泛域名解析即将该域名所有 ...
- MIFARE Classic S50技术详解
Mifare Classic 简介 MIFARE Classic是恩智浦半导体开发的可用于非接触式智能卡,符合ISO/IEC 14443 A类标准.用于公共交通票证等应用,还可用于各类其他应用有S20 ...
- c# json 反序列化 泛型List 2行代码
List<EncyTable> list = new List<EncyTable>(); var jsonReqeust = "[{ENCY_ID:775,ENCY ...
- registry --------->仓库 ----------------->镜像
registry --------->仓库 ----------------->镜像 本地镜像都保存在宿主机下 : /var/lib/docker/containers 镜像从仓库下载下来 ...
- BZOJ3456 城市规划 【分治NTT】
题目链接 BZOJ3456 题解 据说这题是多项式求逆 我太弱不会QAQ,只能\(O(nlog^2n)\)分治\(NTT\) 设\(f[i]\)表示\(i\)个节点的简单无向连通图的数量 考虑转移,直 ...
- 洛谷 P2329 [SCOI2005]栅栏 解题报告
P2329 [SCOI2005]栅栏 题目描述 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购买木材.可是木材店老板说他这里只剩下少部分大规格的木板了 ...