Codeforces 346D Robot Control(01BFS)

题意

有一个 N 个点, M 条边的有向图, 初始有一个机器人在 1 号点. 每个时刻, 这个机器人会随机选择一条从该点出发地边并通过.当机器人到达点 N 时, 它就会自动关闭.

然而这个机器人如果在某个时刻到达自己曾经到过的点的话, 它就会爆炸. 因此, 你决定对机器人实施一些命令, 让它在某些时候按照规定的边走, 而非随机选择.

问对机器人最少使用多少条命令可以让它安全到达点 N .

N,M106

题解

十分巧妙的一道好题~

首先可以无视掉 “不能到达曾经到过的点” 的限制, 因为最优答案一定不会存在这种情况.

因为到达曾经到过的点,你至少要付出更多代价才能回到这个点,所以绝对不优。

然后我们就可以考虑一个 dp 了,令 dpuu 走到 T 需要的最少命令。

那么显然有一个转移:

dpu=min(u,v){min{dpv}+1,max{dpv}}

这个意义是很明显的,就不解释了。

这个本质上是个 0/1 BFS 问题,用个双端队列维护就行了,0 加到队首, 1 加到队尾就行了。

具体实现的时候,我们只有在第一次到达这个点的时候会更新 min{dpv}+1 ,因为是 BFS 最早到的肯定是距离较小的点。

也就是队列中的点 dis 单调不下降。

然后最后一次到达这个点才会更新 max{dpv} ,同样这是 BFS 最晚到的点。

每个点我们只会访问一次,所以最后一次到达就是它入度减少到 0 的时候。

复杂度是 O(n+m) 的。

总结

对于一类图上有关 dpmin,max 问题能考虑 BFS 队列的 dis 单调不下降的性质来转移。

代码

记得要把边反向,以及入度也要反向。

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("D.in", "r", stdin); freopen ("D.out", "w", stdout); #endif } const int N = 1e6 + 1e3; int n, m, deg[N], dp[N]; vector<int> G[N]; int S, T; bitset<N> vis; void Bfs() { Set(dp, -1); deque<int> Q; Q.push_front(T); dp[T] = 0; while (!Q.empty()) { int u = Q.front(); Q.pop_front(); if (u == S) return ; if (vis[u]) continue ; vis[u] = true; for (int v : G[u]) if (!-- deg[v]) { if (!~dp[v] || dp[u] < dp[v]) dp[v] = dp[u], Q.push_front(v); } else if (!~dp[v]) dp[v] = dp[u] + 1, Q.push_back(v); } } int main () { File(); n = read(); m = read(); For (i, 1, m) { int u = read(), v = read(); G[v].push_back(u); ++ deg[u]; } S = read(), T = read(); Bfs(); printf ("%d\n", dp[S]); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9562888.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(570)  评论(5编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示