备战蓝桥杯-双指针、BFS

备战蓝桥杯所有笔记

双指针🦁️、bfs🐯、图论🐰


🦁️日志统计(第九届蓝桥杯)

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 NN 行。

其中每一行的格式是:

ts id  

表示在 tsts 时刻编号 idid 的帖子收到一个”赞”。

现在小明想统计有哪些帖子曾经是”热帖”。

如果一个帖子曾在任意一个长度为 DD 的时间段内收到不少于 KK 个赞,小明就认为这个帖子曾是”热帖”。

具体来说,如果存在某个时刻 TT 满足该帖在 [T,T+D)[T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 KK 个赞,该帖就曾是”热帖”。

给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。

输入格式

第一行包含三个整数 N,D,KN,D,K。

以下 NN 行每行一条日志,包含两个整数 tsts 和 idid。

输出格式

按从小到大的顺序输出热帖 idid。

每个 idid 占一行。

数据范围

1≤K≤N≤1051≤K≤N≤105,
0≤ts,id≤1050≤ts,id≤105,
1≤D≤100001≤D≤10000

输入样例:
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
输出样例:
1
3

分析: 题目要求的是统计某一个时间段内的点赞数目,如果这个时间段内的点赞数目能够超过K那就代表着是一个热评。我们可以用时间进行排序,然后维护一个时间段,很容易能够想到要用双指针的方式控制区间的长度不会超过既定的区间长度,然后注意维护出现的次数就可以了。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <vector>

using namespace std;

const int N = 100010;
typedef pair<int, int> PII; 

PII logs[N];
int cnt[N];
bool istrue[N];

// [T, T + length)  持续时间格式
int n, length, k, a, b;

int main()
{
    // cout << "已经进行了调用...\n";
    ios::sync_with_stdio(0);
    cin >> n >> length >> k;
    
    for (int i = 0; i < n; ++i) {
        cin >> logs[i].first >> logs[i].second;     //输入时间以及对应的ID 
    }
    sort(logs, logs + n);

    for (int r = 0, l = 0; r < n; r++) {
        int id = logs[r].second;        //右窗口的值
        cnt[id] ++;

        while (logs[r].first - logs[l].first >= length) {
            //维持窗口的长度是在length之内
            //左边的窗口向右移动,缩减窗口的长度
            cnt[logs[l++].second]--;
        }
        if (cnt[id] >= k) istrue[id] = true;
    }

    for (int i = 0; i <= 100000; i++) {     //遍历ID
        if (istrue[i]) cout << i << endl;
    }
    
    return 0;
}


🐯献给阿尔吉侬的花束

阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。

今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。

现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。

迷宫用一个 R×CR×C 的字符矩阵来表示。

字符 S 表示阿尔吉侬所在的位置,字符 E 表示奶酪所在的位置,字符 # 表示墙壁,字符 . 表示可以通行。

阿尔吉侬在 1 个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。

输入格式

第一行是一个正整数 TT,表示一共有 TT 组数据。

每一组数据的第一行包含了两个用空格分开的正整数 RR 和 CC,表示地图是一个 R×CR×C 的矩阵。

接下来的 RR 行描述了地图的具体内容,每一行包含了 CC 个字符。字符含义如题目描述中所述。保证有且仅有一个 S 和 E。

输出格式

对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。

若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。

每组数据的输出结果占一行。

数据范围

1<T≤101<T≤10,
2≤R,C≤2002≤R,C≤200

输入样例:
3
3 4
.S..
###.
..E.
3 4
.S..
.E..
....
3 4
.S..
####
..E.
输出样例:
5
1
oop!

一个比较典型的BFS模板级别的题目,使用pair存储xy的坐标,注意细节

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <queue>

#define x first
#define y second

using namespace std;

const int N = 250;

const int dir[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};
typedef pair<int, int> PII;
int T, n, m;
char map[N][N];
int dis[N][N];

int fun (PII start, PII end)
{
    queue<PII> q;
    memset(dis, -1, sizeof dis);
    
    dis[start.x][start.y] = 0;      //设置起点
    q.push(start);
    
    while (q.size()) {
        PII now = q.front();
        q.pop();
        
        for (int i = 0; i < 4; i++) {
            int x = now.x + dir[i][0];
            int y = now.y + dir[i][1];
            if (x < 0 || y < 0 || x >= n || y >= m) continue;
            if (map[x][y] == '#') continue; //墙壁
            if (dis[x][y] != -1) continue;  //之前遍历过
            
            dis[x][y] = dis[now.x][now.y] + 1;      //更新距离
            
            if (end == make_pair(x, y)) return dis[x][y];       //到达终点了
            q.push({x, y});
        }
    }
    return -1;          //到达不了终点,return -1
}


int main()
{
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n >> m;
        for (int i = 0; i < n; i++) cin >> map[i];
        
        PII start, end;
        for (int i = 0; i < n; i++)
            for (int j =  0; j < m; j++) 
            {
                if (map[i][j] == 'S') start = {i, j};
                else if (map[i][j] == 'E') end = {i, j};
            }
        
        int ans = fun(start, end);
        if (ans == -1) cout << "oop!\n";
        else cout << ans << endl;
    }
    return 0;
}
🐰交换瓶子(第七届蓝桥杯)

有 NN 个瓶子,编号 1∼N1∼N,放在架子上。

比如有 55 个瓶子:

2 1 3 5 4

要求每次拿起 22 个瓶子,交换它们的位置。

经过若干次后,使得瓶子的序号为:

1 2 3 4 5

对于这么简单的情况,显然,至少需要交换 22 次就可以复位。

如果瓶子更多呢?你可以通过编程来解决。

输入格式

第一行包含一个整数 NN,表示瓶子数量。

第二行包含 NN 个整数,表示瓶子目前的排列状况。

输出格式

输出一个正整数,表示至少交换多少次,才能完成排序。

数据范围

1≤N≤100001≤N≤10000,

输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2

图论方法不会。。。。

直接暴力了,a[i]应该填充的数是i,枚举一遍,向后寻找数值i,然后交换数值

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 10010;

int n, a[N];
int fins;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i < n; i++) {
        //a[i]应该存储数值:i
        int now = i;            //便于理解
        if(a[i] != now) {                     //那么就找一下后面哪一个内存中存的是now
            int j = now + 1;
            while (a[j] != now) j++;
            swap(a[j], a[now]);
            // a[j] = a[now];
            fins ++;
        }
    } 
    cout << fins;
    return 0;
}

🦁️完全二叉树的权值(第十届蓝桥杯)

给定一棵包含 NN 个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是 A1,A2,···ANA1,A2,···AN,如下图所示:

QQ截图20191205124611.png

现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?

如果有多个深度的权值和同为最大,请你输出其中最小的深度。

注:根的深度是 11。

输入格式

第一行包含一个整数 NN。

第二行包含 NN 个整数 A1,A2,···ANA1,A2,···AN。

输出格式

输出一个整数代表答案。

数据范围

1≤N≤1051≤N≤105,
−105≤Ai≤105−105≤Ai≤105

输入样例:
7
1 6 5 4 3 2 1
输出样例:
2

就是计算每一层的数值之和,每一层的起始位置和每一层应该相加的数量分别用i和d进行维护(都是倍增的)

具体看代码就明白了

#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 100010;
int n;
int a[N];
int main()
{
    cin >> n;
            // 1 2 4 8 16 32 64 128
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    long long maxx = -1e14;                         
    int depth = 1;
    for (int i = 1, d = 1; i <= n; i *= 2, d++ ) {                      //d代表当前层数,i代表开始位置
        long long sum = 0;
        for (int j = i; j < i + (1 << d - 1) && j <= n; j++)            //计算每一层的总和
            sum += a[j];
        if (sum > maxx) {                                               //等于的话不更新,因为要求sum相同,层数低的
            maxx = sum;
            depth = d;
        }
    }
    cout << depth << endl;
    
    return 0;
}
🐯地牢大师

你现在被困在一个三维地牢中,需要找到最快脱离的出路!

地牢由若干个单位立方体组成,其中部分不含岩石障碍可以直接通过,部分包含岩石障碍无法通过。

向北,向南,向东,向西,向上或向下移动一个单元距离均需要一分钟。

你不能沿对角线移动,迷宫边界都是坚硬的岩石,你不能走出边界范围。

请问,你有可能逃脱吗?

如果可以,需要多长时间?

输入格式

输入包含多组测试数据。

每组数据第一行包含三个整数 L,R,CL,R,C 分别表示地牢层数,以及每一层地牢的行数和列数。

接下来是 LL 个 RR 行 CC 列的字符矩阵,用来表示每一层地牢的具体状况。

每个字符用来描述一个地牢单元的具体状况。

其中, 充满岩石障碍的单元格用”#”表示,不含障碍的空单元格用”.”表示,你的起始位置用”S”表示,终点用”E”表示。

每一个字符矩阵后面都会包含一个空行。

当输入一行为”0 0 0”时,表示输入终止。

输出格式

每组数据输出一个结果,每个结果占一行。

如果能够逃脱地牢,则输出”Escaped in x minute(s).”,其中X为逃脱所需最短时间。

如果不能逃脱地牢,则输出”Trapped!”。

数据范围

1≤L,R,C≤1001≤L,R,C≤100

输入样例:
3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0
输出样例:
Escaped in 11 minute(s).
Trapped!

二维BFS的一种三维的拓展,三位的话我们就可以使用struct将数据进行封装起来,具体的实现看代码还是比较清晰的

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;
int n, r, c;
const int N = 105;

char g[N][N][N];
int  t[N][N][N];
int dir[6][3] = {1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1 };

struct Node {
  int x, y, z;
  bool equals(Node n)				//用来判断是否和终点相同
    {
        int res = 1;
        if (x != n.x || y != n.y || z != n.z)res = 0;
        return res;
    }
};



bool check(Node node)			//判断是否出界
{
    if (node.x >= 0 && node.x < r && node.y >= 0 && node.y < c && node.z >= 0 && node.z < n && g[node.z][node.x][node.y] != '#')return true;
    else return false;
}

void bfs(Node s, Node e)
{
    memset(t, -1, sizeof (t));
    queue<Node>q;
    t[s.z][s.x][s.y] = 0;
    q.push(s);
    while (q.size()) {
        auto now = q.front();
        q.pop();
        for (int i = 0; i < 6; i++) {
            Node next = { now.x + dir[i][0] ,now.y+dir[i][1],now.z+dir[i][2]};
            if (t[next.z][next.x][next.y] == -1 && check(next))     //没有遍历过并且在界内
            {
                t[next.z][next.x][next.y] = t[now.z][now.x][now.y] + 1;
                if (next.equals(e)) return;
                q.push(next);
            }
        }
    }
}

int main()
{
    while (cin >> n >> r >> c, n || r || c)         //都执行,返回值由逗号后面的语句确定
    {
        Node s, e;
        for (int i = 0; i < n; i++) 
            for (int j = 0; j < r; j++) 
                cin >> g[i][j];
        for (int i = 0; i < n; i++) 
            for (int j = 0; j < r; j++) 
                for (int k = 0; k < c; k++)
                {
                    if (g[i][j][k] == 'S') s = {j, k, i};
                    if (g[i][j][k] == 'E') e = {j, k, i};
                }
        bfs(s, e);
        if (t[e.z][e.x][e.y] != -1)printf("Escaped in %d minute(s).\n", t[e.z][e.x][e.y]);
        else cout << "Trapped!" << endl;
        
    }
    
    
    return 0;
}
🐯全球变暖(第九届蓝桥杯)

你有一张某海域 N×NN×N 像素的照片,”.”表示海洋、”#”表示陆地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......

其中”上下左右”四个方向上连在一起的一片陆地组成一座岛屿,例如上图就有 22 座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。

具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

输入格式

第一行包含一个整数N。

以下 NN 行 NN 列,包含一个由字符”#”和”.”构成的 N×NN×N 字符矩阵,代表一张海域照片,”#”表示陆地,”.”表示海洋。

照片保证第 11 行、第 11 列、第 NN 行、第 NN 列的像素都是海洋。

输出格式

一个整数表示答案。

数据范围

1≤N≤10001≤N≤1000

输入样例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出样例1:
1
输入样例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
输出样例2:
1

使用bfs进行遍历,一个岛屿淹没的条件:所有的点都是边缘。所以我们在bfs的时候就是要维护好所有点的值和边缘点的值,如果相同的话就会被淹没。

遍历过用bool数组表示而不直接改变值,就能够判断出边缘的值了

#include <iostream>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <queue>
#include <queue>
#define x first 
#define y second 

using namespace std;

typedef pair<int, int> PII;
const int N = 1005;
bool status[N][N];
const int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
int n;
char g[N][N];

void bfs(int x, int y, int &total, int & bound) {
    queue<PII> q;
    q.push({x, y});
    while (q.size()) {
        auto now = q.front();
        q.pop();
        total ++;
        bool is_bound = false;
        for (int i = 0; i < 4; i++) {
            int nx = now.x + dir[i][0];
            int ny = now.y + dir[i][1];
            if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;       //chujie
            if (status[nx][ny]) continue;               //遍历过了
            if (g[nx][ny] == '.') {
                is_bound = true;                    //周边是海洋就是岛屿的边缘
                continue;
            }
            q.push({nx,ny});
            status[nx][ny] = true;
        }
        if (is_bound) bound++;
    }
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> g[i];
    
    int cnt = 0;
    for (int i = 0; i < n; i++) 
        for (int j = 0; j < n; j++) 
        {
            if (g[i][j] == '#' && !status[i][j])    //没遍历过 & 是陆地
            {
                int total  = 0, bound = 0;          //总数 和 边缘数
                bfs(i, j, total, bound);
                if (total == bound) cnt ++;         //总数就是边缘数,淹没
            }
        }
    cout << cnt << endl;
    return 0;
}

🐰大臣的旅费(第四届蓝桥杯子)

很久以前,T王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是T国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。

J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数 nn,表示包括首都在内的T王国的城市数。

城市从 11 开始依次编号,11 号城市为首都。

接下来 n−1n−1 行,描述T国的高速路(T国的高速路一定是 n−1n−1 条)。

每行三个整数 Pi,Qi,DiPi,Qi,Di,表示城市 PiPi 和城市 QiQi 之间有一条双向高速路,长度为 DiDi 千米。

输出格式

输出一个整数,表示大臣J最多花费的路费是多少。

数据范围

1≤n≤1051≤n≤105,
1≤Pi,Qi≤n1≤Pi,Qi≤n,
1≤Di≤10001≤Di≤1000

输入样例:
5 
1  2  2 
1  3  1 
2  4  5 
2  5  4 
输出样例:
135

求无环图的两点之间的最长的路径,也就是求树的直径。任取一点x,找到距离x最远的点y(记录好距离),然后以y为起点,找到距离y最长的距离,两者相比较就是树的直径。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

const int N = 100010;
// vector<vector<int> > V;
struct Edge{
  int dest, len;                //目的地, 距离
};

vector<Edge> v[N];
int dis[N];
int n;

void dfs(int curnode, int fanode,int curlen) {
    dis[curnode] = curlen;
    for (auto & each : v[curnode]) {
        if (each.dest != fanode) {
            dfs(each.dest, curnode, curlen + each.len);
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int a, b, c;
        cin >> a >> b >> c;
        v[a].push_back({b, c});
        v[b].push_back({a, c});
    }
    
    //找到同第一个节点距离最长的节点,将节点同1的距离存储到dis中了
    dfs(1, -1, 0);          
    int finsnode = 1;
    for (int i = 1; i <= n; i++) {
        if (dis[i] > dis[finsnode]) finsnode = i;
    }
    
    dfs(finsnode, -1, 0);
    
    for (int i = 1; i <= n; ++i ) {
        if (dis[i] > dis[finsnode]) finsnode = i;
    }
    
    int fins = dis[finsnode];
    
    printf("%lld", (long long)(21 + fins) * fins / 2);
}

祝大家蓝桥杯取得好成绩😇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值