【YBT2023寒假Day1 A】孤走暗巷(费用流)

孤走暗巷

题目链接:YBT2023寒假Day1 A

题目大意

给你一个整数序列,你要通过一些操作把它变成单调不降序列。
你有 m 种操作,每次可以选择一个长度为 li 的区间,花费 ci 的代价将其加一或减一(是加一还是减一看给你的操作),问你最小代价,或者输出无解。

思路

首先这个区间加减不难想到通过差分变成某两个位置分别加一减一。
然后发现变成单调不降就是中间的部分都是要非 0 数。

看到 n,m 都很小,而且 DP 似乎不太可行(因为有两个位置同时修改),考虑费用流。
然后因为两个位置同时修改的都是 1 的绝对值,而且是相反数,你可以理解为把这个 1 交给了另一个位置。
那你在思考一开始的正负,会发现正的就是它可以给出的最大数量,负的就是它需要的最小数量。
然后因为两边的正负是无所谓的,也就是它可以给出无限的数量。

那我们就把正的跟源点连,负的跟汇点连,两边就是跟源点连流量无限大,都是费用 0
然后再模拟给的操作思考会发现是给的流向收到的,然后连边流量无限费用是给出的值。
不过要注意的是它只是限制的区间的长度,所以你可以直接从左往右滑动那个区间,每个都要连边,边数是 O(nm) 级别没问题。

至于判断是否有解,其实就是看是否刘曼,然后因为源点那边有无限流量的边,我们判断汇点那边的边就行。

代码

#include<queue> #include<cstdio> #include<iostream> #define ll long long #define INF 0x3f3f3f3f3f3f3f3f using namespace std; const int N = 250; const int M = N * N * 20; const int inf = 1e8; struct node { ll x, w, to, nxt, op; }e[M]; ll n, m, S, T, tot, le[M], lee[M], KK; ll dis[M], deg[M], h[N], p[N]; bool in[M]; queue <ll> q; void add(ll x, ll y, ll z, ll w) { e[++KK] = (node){z, w, y, le[x], KK + 1}; le[x] = KK; e[++KK] = (node){0, -w, x, le[y], KK - 1}; le[y] = KK; } bool SPFA() { for (ll i = 0; i <= tot; i++) { dis[i] = INF; in[i] = 0; deg[i] = -1; lee[i] = le[i]; } while (!q.empty()) q.pop(); q.push(S); dis[S] = 0; in[S] = 1; deg[S] = 0; while (!q.empty()) { ll now = q.front(); q.pop(); for (ll i = le[now]; i; i = e[i].nxt) if (e[i].x > 0 && dis[e[i].to] > dis[now] + e[i].w) { dis[e[i].to] = dis[now] + e[i].w; 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; } if (dis[T] == dis[0]) return 0; return 1; } ll dfs(ll now, ll sum) { if (now == T) return sum; in[now] = 1; ll go = 0; for (ll &i = lee[now]; i; i = e[i].nxt) if (!in[e[i].to] && e[i].x > 0 && dis[e[i].to] == dis[now] + e[i].w && deg[e[i].to] == deg[now] + 1) { ll this_go = dfs(e[i].to, min(sum - go, e[i].x)); if (this_go) { e[i].x -= this_go; e[e[i].op].x += this_go; go += this_go; if (go == sum) return go; } } if (go == sum) in[now] = 0; return go; } ll dinic_cost() { ll re = 0; while (SPFA()) { re += dis[T] * dfs(S, INF); } return re; } int main() { freopen("seq.in", "r", stdin); freopen("seq.out", "w", stdout); scanf("%lld %lld", &n, &m); for (int i = 1; i <= n; i++) scanf("%lld", &h[i]); for (int i = 1; i <= n + 1; i++) p[i] = h[i] - h[i - 1]; S = n + 2; T = n + 3; tot = T; for (int i = 1; i <= n + 1; i++) { if (i == 1 || i == n + 1) add(S, i, inf, 0); else if (p[i] > 0) add(S, i, p[i], 0); else if (p[i] < 0) add(i, T, -p[i], 0); } for (int i = 1; i <= m; i++) { char op = getchar(); while (op != '+' && op != '-') op = getchar(); int l, c; scanf("%d %d", &l, &c); if (l == n) continue; for (int j = 1; j + l <= n + 1; j++) { if (op == '-') add(j, j + l, inf, c); else add(j + l, j, inf, c); } } ll ans = dinic_cost(); for (int i = le[T]; i; i = e[i].nxt) { if (e[e[i].op].x) { printf("-1"); return 0; } } printf("%lld", ans); return 0; }

__EOF__

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