设
重要的图搜索算法有两个:广度优先搜索和深度优先搜索。
设
队列里的点到起点的距离相差不超过
写法一
const int maxn = 1e5 + 5;
vector<int> g[maxn];
int d[maxn];
void init() {
memset(d, -1, sizeof d);
}
void bfs(int s) {
queue<int> q;
q.push(s);
d[s] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : g[u])//扩展
if (d[v] == -1) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
写法二
const int maxn = 1e5 + 5;
vector<int> g[maxn];
int d[maxn];
bool vis[maxn];
struct Path { int to, len; };
void bfs(int s) {
queue<Path> q;
q.push({s, 0});
while (!q.empty()) {
auto t = q.front();
q.pop();
if (vis[t.to]) continue;
vis[t.to] = true;
d[t.to] = t.len;
for (int v : g[t.to])//扩展
q.push({v, t.len + 1});
}
}
给你正整数
保证有解。
把只含有数字
以
考虑以下有向图
struct X {
int r;
string s;
};
void bfs(int n) {
vector<bool> vis(n);
queue<X> q;
q.push({1 % n, "1"});
while (!q.empty()) {
X t = q.front(); q.pop();
if (vis[t.r]) continue;
vis[t.r] = true;
if (t.r == 0) {
cout << t.s << '\n';
break;
}
for (int i : {0, 1})
q.push({(t.r * 10 + i) % n, t.s + char('0' + i)});
}
}
有一个尺寸是
岛被划成
从现在开始海平面每年上升
跟海水或者已被淹没的格子相邻并且不高于海平面的格子会被淹没。
给定正整数
10 2 10
3 1 4
10 5 10
一年后,没有格子被淹没,有
两年后,有
10 x 10
3 x 4
10 5 10
三年后,有
10 x 10
x x 4
10 5 10
四年以后,有
10 x 10
x x x
10 5 10
五年以后,有
10 x 10
x x x
10 x 10
对于
找出那些跟
这些格子首当其冲,随后向内传染,使跟它们连通并且海拔高度不超过
const int maxa = 1e5+5;
struct GZ { int r, c; }; //格子
vector<GZ> g[maxa];
int a[1002][1002];
bool sink[1002][1002];
int main() {
int H, W, Y; cin >> H >> W >> Y;
for (int i = 1; i <= H; i++)
for (int j = 1; j <= W; j++) {
cin >> a[i][j];
g[a[i][j]].push_back({i, j});
}
for (int i = 0; i <= H + 1; i++)
for (int j = 0; j <= W + 1; j++)
if (i == 0 || j == 0 || i == H + 1 || j == W + 1)
sink[i][j] = true;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int ans = H * W;
for (int i = 1; i <= Y; i++) {
queue<GZ> q;
for (GZ p: g[i])
for (int j = 0; j < 4; j++)
if (sink[p.r + dir[j][0]][p.c + dir[j][1]]) { q.push(p); break; }
// BFS
while (!q.empty()) {
GZ p = q.front(); q.pop();
if (sink[p.r][p.c]) continue;
sink[p.r][p.c] = true; ans--;
for (int j = 0; j < 4; j++)
if (a[p.r + dir[j][0]][p.c + dir[j][1]] <= i)
q.push({p.r + dir[j][0], p.c + dir[j][1]});
}
cout << ans << '\n';
}
}
给定有
求
保证存在满足条件的四个点。
const int maxn = 2500 + 5;
int n, m, k;
vector<int> g[maxn];
long long p[maxn];
vector<int> s[maxn];
int d[maxn][maxn];
const int INF = 1e9;
void bfs(int s) {
for (int i = 1; i <= n; i++)
dist[s][i] = INF;
queue<int> q;
q.push(s);
d[s][s] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : g[u])
if (d[s][v] == INF) {
d[s][v] = d[s][u] + 1;
q.push(v);
}
}
}
bool cmp(int i, int j) {
return p[i] > p[j];
}
int main() {
cin >> n >> m >> k;
for (int i = 2; i <= n; i++) cin >> p[i];
for (int i = 0; i < m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v); g[v].push_back(u);
}
for (int i = 1; i <= n; i++) bfs(i);
vector<int> a;
for (int i = 2; i <= n; i++)
if (d[1][i] <= k + 1) a.push_back(i);
sort(a.begin(), a.end(), cmp);
for (int i = 2; i <= n; i++)
for (int v : a)
if (v != i && d[i][v] <= k + 1) {
cand[i].push_back(v);
if (cand[i].size() == 3) break;
}
long long ans = 0;
for (int c = 2; c <= n; c++)
for (int d = c + 1; d <= n; d++)
if (d[c][d] <= k + 1)
for (int b : cand[c])
for (int e : cand[d])
if (b != d && e != c && b != e)
ans = max(ans, p[b] + p[c] + p[d] + p[e]);
cout << ans << '\n';
}
某景区有
有
小 Z 希望乘坐巴士到达入口,走到出口,再乘坐巴士离开,因此他到达和离开景区时间都必须是
求小 Z 可能离开景区的最早时间。
如果在所有道路都开放之后进入景区,那么问题化为在一个有
样例:
从
对于每个时刻
在上述有
注意到
struct E {int to, a;};
const int maxn = 1e4 + 5;
const int maxa = 1e6 + 5;
vector<E> g[maxn];
vector<int> V[2 * maxa + 100];
bool vis[maxn][100];
int n, m, k;
int bfs() {
V[0].push_back(1);
for (int t = 0; t < 2e6 + 100; t++)
// 从t时刻能到的点向(时间上地)后扩展
for (int u : V[t]) {
if (vis[u][t % k]) continue;
vis[u][t % k] = 1;
if (u == n && t % k == 0)
return t;
for (E e : g[u])
if (e.a <= t) V[t + 1].push_back(e.to);
else {
int nt = t + (e.a - t + k - 1) / k * k + 1;
V[nt].push_back(e.to);
}
}
return -1;
}
BFS 常用来计算从一个状态到另一个状态最少需要走几步。在一些问题中有的走法算一步,有的走法不算步数。用图论的语言来描述:设
上述问题也可以用 BFS 来解决:当顶点
为此我们要把普通 DFS 中使用的队列换为双端队列:可以在开头或结尾添加或弹出元素的队列。
C++ 标准库里有现成的双端队列 std::deque。
支持下列队列操作
实际上,deque 和 vector 相似,是一种通用的序列容器,也支持用下标访问元素。不过 deque 的结构比 vector 复杂,常数也较大;所以通常我们只把它用作双端队列。
有一个
高桥要从左上角的格子走到右下角的格子。每一步他可走到上下左右相邻且可通过的格子里去。他不能走出网格,也不能走到障碍物格子里去。不过,高桥力气很大,他打一拳能把一个
求高桥从左上角的格子走到右下角的格子至少要打几拳。
输入
5 5
..#..
#.#.#
##.##
#.#.#
..#..
输出:1
..#..
#.**#
##**#
#.#.#
..#..
假设高桥已经确定了路线。他打拳的目标是消除路线上的障碍物。沿着既定的路线走,遇到障碍物时才考虑如何打一拳把它消除。
struct point {
int r, c, d;
};
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
bool vis[500][500];
//网格行列编号从0开始
char s[500][505];
int h, w;
int main() {
int h, w;
cin >> h >> w;
for (int i = 0; i < h; i++)
cin >> s[i];
01_bfs();
return 0;
}
void 01_bfs() {
deque<point> q;
q.push_back({0, 0, 0});
while (!q.empty()) {
auto cur = q.front(); q.pop_front();
if (vis[cur.r][cur.c]) continue;
vis[cur.r][cur.c] = true;
if (cur.r == h - 1 && cur.c == w - 1) {
cout << cur.d; return;
}
vis[cur.r][cur.c] = true;
for (int i = 0; i < 4; i++) {
int r = cur.r + dir[i][0], c = cur.c + dir[i][1];
if (0 <= r && r < h && 0 <= c && c < w) {
if (s[r][c] == '.') {
q.push_front({r, c, cur.d});
} else {// s[r][c] == '#'
for (int dr = -1; dr <= 1; dr++)
for (int dc = -1; dc <= 1; dc++) {
int nr = r + dr, nc = c + dc;
if (0 <= nr && nr < h && 0 <= nc && nc < w)
q.push_back({nr, nc, cur.d + 1});
}
}
}
}
}
}