【YBT2022寒假Day6 B】【luogu CF802C】大图书馆 / Heidi and Library (hard)(网络流)

大图书馆 / Heidi and Library (hard)

题目链接:YBT2022寒假Day6 B / luogu CF802C

题目大意

你有一个容量为 k 的桶,然后每天有需求要一个编号的东西,这天结束时会还回来。
然后你可以花一定的钱买入一个编号的东西,或者把东西扔掉,买的东西不能超过容量数,然后问你最小花费。

思路

考虑网络流建图。

首先是每天要买,源点连向点流量 1 费用为买的花费,然后是提供,每天都要提供,所以点想汇点连流量为 1费用为 0
那你会一个提供多个啊,你网络流无法实现啊。

那我们考虑这样,我们肯定每个都买,然后我们弄一个类似卖的操作。即如果之前有这个,那我们就可以走一个卖的边来退钱。

那我们考虑拆点,前面的就源点留到拆出的第一个点,第一个点连流量 1 费用 0 点到第二个点,再连到汇点。
然后保留的操作我们就每个点拆出的第一个点顺着连过去,流量是 k1 费用是 0
(这里是 k1 是因为你当日要买入给今天用的,所以就一定要占一个位置,而且这样的话我们要保留这个种类的话保留到它前一天就可以结束了)

然后就是卖出了,因为上面这句话,所以我们是从这个点的上一个点拆出来的第一个连向这个种类上一次出现的点拆出来的第二个,费用是这次的费用的相反数,流量是 1

然后跑费用流就可以得出结果了。

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; struct node { int x, to, nxt, cst, op; }e[1000001]; int n, k, c[1001], tot, S, T, lee[2501]; int lst[1001], a[1001], le[2501], KK; void add(int x, int y, int z, int va) { e[++KK] = (node){z, y, le[x], va, KK + 1}; le[x] = KK; e[++KK] = (node){0, x, le[y], -va, KK - 1}; le[y] = KK; } queue <int> q; int dis[2501], deg[2501]; bool in[2501]; bool SPFA() { memset(dis, 0x7f, sizeof(dis)); memset(deg, 0, sizeof(deg)); memcpy(lee, le, sizeof(lee)); q.push(S); dis[S] = 0; in[S] = 1; deg[S] = 1; while (!q.empty()) { int now = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].x && dis[e[i].to] > dis[now] + e[i].cst) { dis[e[i].to] = dis[now] + e[i].cst; deg[e[i].to] = deg[now] + 1; if (!in[e[i].to]) { in[e[i].to] = 1; q.push(e[i].to); } } in[now] = 0; } return dis[T] != dis[0]; } int dfs(int now, int sum) { if (now == T) return sum; int go = 0; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (e[i].x && dis[e[i].to] == dis[now] + e[i].cst && deg[e[i].to] == deg[now] + 1 && !in[e[i].to]) { int this_go = dfs(e[i].to, min(e[i].x, sum - go)); if (this_go) { e[i].x -= this_go; e[e[i].op].x += this_go; go += this_go; if (go == sum) {in[now] = 0; return go;} } } if (go != sum) dis[now] = -1; in[now] = 0; return go; } pair <int, int> dinic() { pair <int, int> re = make_pair(0, 0); while (SPFA()) { int x = dfs(S, INF); re.first += x; re.second += dis[T] * x; } return re; } int main() { // freopen("bibliotheca.in", "r", stdin); // freopen("bibliotheca.out", "w", stdout); scanf("%d %d", &n, &k); tot = 2 * n; S = ++tot; T = ++tot; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 1; i <= n; i++) { scanf("%d", &c[i]); } for (int i = 1; i <= n; i++) { add(S, i, 1, c[a[i]]); add(i + n, T, 1, 0); add(i, i + n, 1, 0); if (lst[a[i]]) add(i - 1, lst[a[i]] + n, 1, -c[a[i]]); lst[a[i]] = i; if (i != n) add(i, i + 1, k - 1, 0); } pair <int, int> re = dinic(); if (re.first != n) return -1; else printf("%d", re.second); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2022Day6_B.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示