蓝桥杯 第六讲 双指针、BFS和图论

一、双指针(滑动窗口)

1238. 日志统计

以D为单位枚举时间段,删去区间开头时间的帖子数量,加上区间结尾的帖子数量
image

#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 (多数为入队时判重)

image
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. 交换瓶子

向该瓶子应该放的位置上的瓶子连一条边
image
image
最终证明将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;
}

三、树的直径(最长路径)

image

图的表示

邻接表的vector存储法

一维vector:vector<Edge> h[N] h[a].push_back({id,w})
1207. 大臣的旅费
image

法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;
}
posted @   安河桥北i  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示