【luogu P7417】Minimizing Edges P(贪心)(思维)

Minimizing Edges P

题目链接:luogu P7417

题目大意

给你一个图,然后 fi,j 表示是否存在一个从 1 到 i 的路径经过的边数是 j。
然后要你构造出来一个边数最少的图使得它的 f 函数跟给出的图的一样。
输出最小边数。
会有自环,没有重边。

思路

首先不难想到它可以在一条边上反复横跳。

所以会影响这个函数的就只有到每个点的最短奇数长度路径和最短偶数长度路径。
这个可以 bfs 求出,不过我用的是 dij,而且我是把点拆成奇数长度到的点和偶数长度到的点来跑的。

然后接着你考虑怎么构新图。
首先如果 1 有自环,就是直接 n 条边。(一个自环,然后剩下的搞树)
还有一种比较特殊的就是二分图。
那其实就是直接 n1 搞成树就可以了。

那接着就是剩下的,那我们想一个点要从那些点满足,然后连边过来。
不难想到这时候奇偶就没有太大所谓了,我们就直接 (a,b) 表示两个距离是 (a,b) 的状态。
而且为了方便我们让 a<b
(至于这个状态我们可以用 map)

不难想到,两种可能:
(a,b)(a1,b1) 转。
(a,b)(a1,b+1)(a+1,b1) 两个一起转。

那你可以 a+b 看做一维,a 看做一维,那第二种就相当于内部消化,第一种就相当于往上转移。

那如果只有一种,那肯定就是从那个转。
第一个的转很好想,就是直接加,但是第二个呢?
我们考虑搞一个 cntx,y 表示 (x,y) 这个状态从左边转了多少个过来。(这个也是用 map 存)
然后那你转 (x,y) 的时候就把转两边的个数加到 (x+1,y1) 里面,然后你转 (x+1,y1) 的时候要转的个数就少了 cntx+1,y1

然后不难想到会出现一个状态就是 x+1=y,那就是可以两个碰在一起消掉,那相当于你两个如果有两个的话就可以只用一条边,那费用就变成了 (nmx,y+1)/2

然后如果两个都有就是转两边的就继续转两边,剩下的如果还要转的就一定往上转。
(因为往上转是更优的,显然)

然后你跑的时候记录一下答案,最后输出就可以了。
(这么一看这题除了贪心好像没有什么算法但是就是很阴间)

代码

#include<map> #include<queue> #include<cstdio> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; const int N = 200001; struct node { int to, nxt; }e[N << 2]; int T, n, m, x, y, le[N << 1], KK; int ans, ax[N << 1], ay[N << 1]; struct st { int x, y; }tp[N]; map <pair<int, int>, int> nm, hv; int tpn; struct ztzt { int dis, now; }; bool operator <(ztzt x, ztzt y) { return x.dis > y.dis; } priority_queue <ztzt> q; int dis[N << 1]; bool in[N << 1]; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } void dij() {//dij 跑出奇路径和偶路径的最短路(其实 bfs 都可以) while (!q.empty()) q.pop(); dis[0] = INF; for (int i = 1; i <= n; i++) { dis[i] = dis[i + n] = INF; in[i] = in[i + n] = 0; } dis[1] = 0; q.push((ztzt){0, 1}); while (!q.empty()) { int now = q.top().now; q.pop(); if (in[now]) continue; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (!in[e[i].to] && dis[e[i].to] > dis[now] + 1) { dis[e[i].to] = dis[now] + 1; q.push((ztzt){dis[e[i].to], e[i].to}); } } } bool cmp1(st x, st y) { if (x.x + x.y != y.x + y.y) return x.x + x.y < y.x + y.y; return x.x < y.x; } int main() { scanf("%d", &T); while (T--) { scanf("%d %d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d %d", &x, &y); ax[i] = x; ay[i] = y; add(x, y + n); add(x + n, y); } dij(); if (dis[1 + n] == 1) {//起点有自环 printf("%d\n", n); } else { bool gogo = 0; for (int i = 1; i <= n; i++) {//特判二分图 if (dis[i] == dis[0] || dis[i + n] == dis[0]) { gogo = 1; break; } } if (gogo) { printf("%d\n", n - 1); } else { ans = 0; tpn = 0; nm.clear(); hv.clear(); for (int i = 1; i <= n; i++) { pair <int, int> t = make_pair(dis[i], dis[i + n]); if (t.first > t.second) swap(t.first, t.second); nm[t]++; tp[++tpn] = (st){t.first, t.second}; } sort(tp + 1, tp + tpn + 1, cmp1); for (int i = 1; i <= tpn; i++) { if (tp[i].x == tp[i - 1].x && tp[i].y == tp[i - 1].y) continue; x = tp[i].x; y = tp[i].y; pair <int, int> t = make_pair(x, y), t1 = make_pair(x - 1, y - 1); pair <int, int> t2 = make_pair(x - 1, y + 1), t3 = make_pair(x + 1, y - 1); if (nm[t2] == 0 && nm[t1] != 0) {//直接连 (x-1,y-1) ans += nm[t]; } else if (nm[t2] != 0 && nm[t1] == 0) {//直接连 (x-1,y+1) ans += max(0, nm[t] - hv[t]);//只用连剩下的就可以了 if (x + 1 == y) ans += (nm[t] + 1) / 2;//在中间消化了 else {//继续往右移 ans += nm[t]; hv[t3] += nm[t]; } } else if (nm[t2] != 0 && nm[t1] != 0) {//两个都要 ans += max(0, nm[t] - hv[t]); hv[t] = min(hv[t], nm[t]);//记得这个要变 if (x + 1 == y) ans += (hv[t] + 1) / 2;//跟上面同理 else { ans += hv[t]; hv[t3] += hv[t]; } } } printf("%d\n", ans); } } KK = 0; for (int i = 1; i <= n; i++) { le[i] = le[i + n] = 0; } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P7417.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示