蓝桥杯 第六讲 双指针、BFS和图论
一、双指针(滑动窗口)
1238. 日志统计
以D为单位枚举时间段,删去区间开头时间的帖子数量,加上区间结尾的帖子数量
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
PII logs[N];
int n,d,k,ts,id;
int cnt[N]; //k区间内获得的点赞数
bool st[N]; //是否在d时间跨度内是热帖
int main()
{
scanf("%d%d%d", &n, &d,&k);
for (int i = 0; i < n; i ++ )
{
scanf("%d%d", &ts, &id);
logs[i] = {ts,id};
}
sort(logs,logs+n); //按照时间升序排序
for (int i = 0,j = 0; i < n; i ++ ) //双指针,i在右,j在左,控制区间内的日志的时间跨度不超过d
{
id = logs[i].second;
++cnt[id];//
while(logs[i].first - logs[j].first >= d) //控制区间内的日志的时间跨度不超过d
{
--cnt[logs[j].second];
++j;
}
if(cnt[id] >= k) st[id] = true;
}
for (int i = 0; i <= N; i ++ )
{
if(st[i]) cout<<i<<endl;
}
return 0;
}
二、BFS (多数为入队时判重)
BFS可以找到最短路径,也可以用来判断是否存在环路(判重数组)
1101. 献给阿尔吉侬的花束
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 205,INF = 0x3f3f3f3f;
typedef pair<int, int> PII;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char g[N][N];
int cnt[N][N];
bool st[N][N];
int n,m,T;
PII s,d;
int bfs()
{
queue<PII>q;
q.push(s);
st[s.first][s.second] = true;
cnt[s.first][s.second] = 0;
while(!q.empty())
{
PII t = q.front();
q.pop();
int x = t.first,y = t.second;
for(int i = 0;i < 4;i++)
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<0 || nx>=n ||ny<0 || ny>=m || st[nx][ny] || g[nx][ny] == '#') continue;
q.push({nx,ny});
st[nx][ny] = true;
cnt[nx][ny] = cnt[x][y] + 1;
}
}
return cnt[d.first][d.second];
}
int main()
{
scanf("%d", &T);
while (T -- )
{
memset(cnt,0,sizeof cnt);
memset(st,false,sizeof st);
memset(cnt,INF,sizeof cnt);
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i++)
{
cin>>g[i];
for(int j = 0;j < m;j++)
{
if(g[i][j] == 'S') s = {i,j};
else if(g[i][j] == 'E') d = {i,j};
}
}
int ans = bfs();
if(ans == INF) cout<<"oop!"<<endl;
else cout<<ans<<endl;
}
return 0;
}
1113. 红与黑
BFS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 25,INF = 0x3f3f3f3f;
int n,m;
char g[N][N];
int step[N][N];
int dx[4] = {-1,1,0,0},dy[4] = {0,0,-1,1};
PII s;
int bfs()
{
int cnt = 0;
queue<PII>q;
q.push(s);
step[s.first][s.second] = ++cnt;
while(!q.empty())
{
PII t = q.front();
q.pop();
int x = t.first,y = t.second;
for (int i = 0; i < 4; i ++ )
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<0||nx>=n||ny<0||ny>=m||step[nx][ny]!=-1||g[nx][ny] == '#') continue;
q.push({nx,ny});
step[nx][ny] = ++cnt;
}
}
return cnt;
}
int main()
{
while(true)
{
memset(step,-1,sizeof step);
scanf("%d%d", &m, &n);
if(n == 0 && m == 0) break;
for (int i = 0; i < n; i ++ )
{
scanf("%s",g[i]);
for (int j = 0; j < m; j ++ )
{
if(g[i][j] == '@')
{
s = {i,j};
}
}
}
cout<<bfs()<<endl;
}
return 0;
}
DFS
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
int sx,sy;
const int N = 25;
char g[N][N];
bool st[N][N];
int n,m;
int ans;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void dfs(int x,int y)
{
st[x][y] = true;
++ans;
for (int i = 0; i < 4; i ++ )
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<0||nx>=n||ny<0||ny>=m||st[nx][ny]||g[nx][ny] == '#') continue;
dfs(nx,ny);
}
}
int main()
{
while(true)
{
memset(st,false,sizeof st);
ans = 0;
scanf("%d%d", &m, &n);
if(n == 0 && m == 0) break;
for (int i = 0; i < n; i ++ )
{
scanf("%s",g[i]);
for (int j = 0; j < m; j ++ )
{
if(g[i][j] == '@')
{
sx = i;
sy = j;
}
}
}
dfs(sx,sy);
cout << ans <<endl;
}
return 0;
}
AcWing 1224. 交换瓶子
向该瓶子应该放的位置上的瓶子连一条边
最终证明将k个环变成n个环,至少需要n-k次交换,因此本题需要统计有几个环,即可求出交换次数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10005;
int a[N],n;
int cnt;
bool st[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i ++ )
{
if(!st[i]) //发现未遍历的环
{
++cnt;
for(int j = i;st[j]!=true;j = a[j]) //遍历这个环,并标记
{
st[j] = true;
}
}
}
cout << n - cnt << endl; //将cnt个环变成n个自环,最少需要 n - cnt 次 交换操作
return 0;
}
1240. 完全二叉树的权值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int a[N],n;
int d = 1,cnt,i;
LL Max = -1e5,Maxd = 0,sum;
int main()
{
scanf("%d", &n);
for (i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
}
i = 1;
for(d = 1;i<=n;d++) //枚举depth层数
{
sum = 0;
for (i = 1<<(d-1); i + 1<= 1<<d && i<=n; i ++ ) //注意完全二叉树最后一层可能不全,必须加i<=n
{
sum += a[i];
++cnt;
}
if(sum > Max)
{
Max = sum;
Maxd = d;
}
}
cout << Maxd <<endl;
return 0;
}
1096. 地牢大师
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 105, INF = 0x3f3f3f3f;
typedef pair<pair<int, int>, int> PIII;
int dx[6] = { 0,0,0,0,-1,1 }, dy[6] = { 0,0,-1,1,0,0 }, dz[6] = { -1,1,0,0,0,0 };
int l, r, c;
int sx, sy, sz, ex, ey, ez;
char g[N][N][N];
int step[N][N][N];
bool st[N][N][N];
int bfs()
{
queue<PIII>q;
q.push({ {sx,sy},sz });
step[sx][sy][sz] = 0;
while (!q.empty())
{
PIII t = q.front();
q.pop();
int x = t.first.first, y = t.first.second, z = t.second;
for (int i = 0; i < 6; i++)
{
int nx = x + dx[i], ny = y + dy[i], nz = z + dz[i];
if (nx < 0 || nx >= l || ny < 0 || ny >= r || nz < 0 || nz >= c || step[nx][ny][nz] != INF || g[nx][ny][nz] == '#') continue;
q.push({ {nx,ny},nz });
st[nx][ny][nz] = true;
step[nx][ny][nz] = step[x][y][z] + 1;
}
}
return step[ex][ey][ez];
}
int main()
{
while (true)
{
memset(step, INF, sizeof step);
scanf("%d%d%d", &l, &r, &c);
if (l == 0 && r == 0 && c == 0) return 0;
//getchar();
for (int i = 0; i < l; i++)
{
for (int j = 0; j < r; j++)
{
scanf("%s", g[i][j]);
for (int k = 0; k < c; k++)
{
if (g[i][j][k] == 'S')
{
sx = i, sy = j, sz = k;
}
else if (g[i][j][k] == 'E')
{
ex = i, ey = j, ez = k;
}
}
}
getchar();
}
int ans = bfs();
if (ans != INF)
{
printf("Escaped in %d minute(s).\n", ans);
}
else puts("Trapped!");
}
return 0;
}
1233. 全球变暖
BFS+Flood Fill
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1005;
typedef pair<int, int> PII;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char g[N][N];
int n,nx,ny,ans,cnt;
bool vis[N][N];
bool st;//该岛屿是否会被淹没
void bfs(int sx,int sy)
{
queue<PII>q;
q.push({sx,sy});
vis[sx][sy] = true;
while(!q.empty())
{
PII t = q.front();
q.pop();
int x = t.first,y = t.second;
if(g[x-1][y] == '#' && g[x+1][y] == '#' && g[x][y-1] == '#' && g[x][y+1] == '#') st = false;
for (int i = 0; i < 4; i ++ )
{
int nx = x + dx[i];
int ny = y + dy[i];
if(nx<0||nx>=n||ny<0||ny>=n||vis[nx][ny]||g[nx][ny] == '.') continue;
q.push({nx,ny});
vis[nx][ny] = true;
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%s", g[i]);
}
for (int i = 0; i < n; i ++ ) //flood fill
{
for (int j = 0; j < n; j ++ )
{
st = true;//假设它会被淹没
if(vis[i][j] || g[i][j] == '.') continue;
bfs(i,j);
++cnt;//统计所有岛屿的个数,即连通分量
if(!st) ans++;//如果没有被淹没
}
}
cout << cnt - ans <<endl;//所有岛屿的个数 减去 没被淹的
return 0;
}
DFS + Flood Fill
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1005;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char g[N][N];
int n,nx,ny,ans,cnt;
bool vis[N][N];
bool st;//该岛屿是否会被淹没
void dfs(int x,int y)
{
vis[x][y] = true;
if(g[x+1][y] == '#' && g[x-1][y] == '#' && g[x][y-1] == '#' && g[x][y+1] == '#') st = false;//四处都是岩石,不会被淹没
for (int i = 0; i < 4; i ++ )
{
nx = x + dx[i],ny = y + dy[i];
if(nx<0 ||nx>=n||ny<0||ny>=n||vis[nx][ny]||g[nx][ny] == '.') continue;
dfs(nx,ny);
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%s", g[i]);
}
for (int i = 0; i < n; i ++ ) //flood fill
{
for (int j = 0; j < n; j ++ )
{
st = true;//假设它会被淹没
if(vis[i][j] || g[i][j] == '.') continue;
dfs(i,j);
++cnt;//统计所有岛屿的个数,即连通分量
if(!st) ans++;//如果没有被淹没
}
}
cout << cnt - ans <<endl;//所有岛屿的个数 减去 没被淹的
return 0;
}
三、树的直径(最长路径)
图的表示
邻接表的vector存储法
一维vector:vector<Edge> h[N]
h[a].push_back({id,w})
1207. 大臣的旅费
法1:两次DFS vector邻接表表示
必须进行两次DFS,才能求出任何一个点到另外一个点的最长距离
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 1e5 + 10;
vector<PII>h[N];
int n,a,b,w;
int dist[N]; //记录起点到其他任何点的距离
void dfs(int u,int father,int distance)
{
dist[u] = distance;
for(int i=0;i<h[u].size();i++)
{
if(h[u][i].first!=father) //无向边,避免向回重复遍历
{
dfs(h[u][i].first,u,distance + h[u][i].second);
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n-1; i ++ )
{
scanf("%d%d%d", &a, &b,&w);
h[a].push_back({b,w});
h[b].push_back({a,w});
}
dfs(1,-1,0);
int Max = -1,Maxu = -1;
for (int i = 1; i <= n; i ++ )
{
if(dist[i] > Max)
{
Max = dist[i];
Maxu = i;
}
}
dfs(Maxu,-1,0); //从当前距离最长的点向其他点遍历
for (int i = 1; i <= n; i ++ )
{
if(dist[i] > Max)
{
Max = dist[i];
Maxu = i;
}
}
LL ans = (LL)1.0*(21+Max)*Max/2;
cout << ans << endl;
}
法2:两次DFS 单链表数组表示邻接表
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int h[N*2],ne[N*2],e[N*2],w[N*2],idx;
int n,a,b,c;
int dist[N]; //记录起点到其他任何点的距离
void add(int a,int b,int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u,int father,int distance)
{
dist[u] = distance;
for(int i = h[u];i!=-1;i = ne[i])
{
if(e[i]!= father)
{
dfs(e[i],u,distance + w[i]);
}
}
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 0; i < n-1; i ++ )
{
scanf("%d%d%d", &a, &b,&c);
add(a, b, c);
add(b, a, c);
}
dfs(1,-1,0);
int Max = -1,Maxu = -1;
for (int i = 1; i <= n; i ++ )
{
if(dist[i] > Max)
{
Max = dist[i];
Maxu = i;
}
}
dfs(Maxu,-1,0); //从当前距离最长的点向其他点遍历
for (int i = 1; i <= n; i ++ )
{
if(dist[i] > Max)
{
Max = dist[i];
Maxu = i;
}
}
LL ans = (LL)1.0*(21+Max)*Max/2;
cout << ans << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)