蓝桥杯赛前冲刺-双指针和图论专题(包含历年蓝桥杯真题和详细注释代码)

日志统计(第九届蓝桥杯省赛C++B组,第九届蓝桥杯省赛JAVAB组)

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 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
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 1e5+10;
typedef pair<int, int> PII;
PII logs[N];//存下输入的日志
int cnt[N];//cnt[i]当前区间内i号帖子被访问的次数
bool st[N];//是否是热帖

int main()
{
    int n,d,k;
    cin>>n>>d>>k;
    for(int i=0;i<n;i++)cin>>logs[i].x>>logs[i].y;
    sort(logs,logs+n);//对帖子点赞按时间进行排序
    for(int r=0,l=0;r<n;r++)//维护时间[l,r)区间内的点赞数量
    {
        cnt[logs[r].y]++;//当前帖子数量+1
        while(logs[r].x-logs[l].x>=d){//当(logs[r].x-logs[l].x>=d 代表 当前时间区间已经不符合条件 因此开始维护 [l+1,r+1)区间
            cnt[logs[l].y]--;//将l区间的计数进行扣除
            l++;//让左指针往前走
        }
        if(cnt[logs[r].y]>=k)st[logs[r].y]=true;//只要一次 在区间内部帖子点赞数大于等于k 即是热帖 做个标识 
    }
    
    for(int i=0;i<100000;i++)if(st[i])cout<<i<<endl;//按照标识 遍历输出答案 100000是数据范围得来
    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!
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;
const int N=210;
typedef pair<int,int>PII;
char g[N][N];//标记地图
int dist[N][N];//标记起点到(i,j)的距离
int n,m;
int bfs(PII start,PII end)
{
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    memset(dist,-1,sizeof dist);//初始化距离
    dist[start.x][start.y]=0;//起点距离为0
    queue<PII>q;
    q.push(start);//将起点加入队列
    while(q.size())
    {
        auto t=q.front();//取出队列元素
        if(t==end)return dist[end.x][end.y];//如果取出元素就是终点 直接返回距离
        q.pop();
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];//记录上下左右的坐标
            if(x<0||x>=n||y<0||y>=m)continue;//出界
            if(g[x][y]=='#')continue;//遇到障碍
            if(dist[x][y]!=-1)continue;//已经走到过
            dist[x][y]=dist[t.x][t.y]+1;//距离+1
            q.push({x,y});//加入队列
        }
    }
    return -1;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        PII start,end;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
            {
                cin>>g[i][j];
                //记录起点坐标和终点坐标
                if(g[i][j]=='S')start={i,j};
                if(g[i][j]=='E')end={i,j};
            }
            //进行bfs
        int ans=bfs(start,end);
        if(ans==-1)cout<<"oop!"<<endl;
        else cout<<ans<<endl;
    }
    return 0;
}

 红与黑

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。

你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。

请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

输入格式

输入包括多个数据集合。

每个数据集合的第一行是两个整数 WW 和 HH,分别表示 xx 方向和 yy 方向瓷砖的数量。

在接下来的 HH 行中,每行包括 WW 个字符。每个字符表示一块瓷砖的颜色,规则如下

1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

输出格式

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

数据范围

1≤W,H≤201≤W,H≤20

输入样例:

6 9 
....#. 
.....# 
...... 
...... 
...... 
...... 
...... 
#@...# 
.#..#. 
0 0

输出样例:

45
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25;
int n,m;
char g[N][N];//标记地图
bool st[N][N];//标记是否走过
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int dfs(int x,int y)
{
    int cnt=1;//cnt用来计算能走到的个数 当前自己+1
    st[x][y]=true;//表示已经走过
    for(int i=0;i<4;i++)
    {
        int a=x+dx[i],b=y+dy[i];
        if(a<0||a>=n||b<0||b>=m)continue;//越界
        if(g[a][b]!='.')continue;//无法到达
        if(st[a][b])continue;//已经走过
        cnt+=dfs(a,b);//加上符合的个数
    }
    return cnt;//返回结果
}
int main()
{
    
    while(cin>>m>>n,n||m)
    {
        for(int i=0;i<n;i++)cin>>g[i];
        int x,y;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(g[i][j]=='@'){
                    //寻找最开始的结点
                    x=i,y=j;
                }
            }
        }
        memset(st, false, sizeof st);//因为有可能不止读取一次 所以要重置st
        cout<<dfs(x,y)<<endl;//进行dfs
    }
    return 0;
}

 交换瓶子(第七届蓝桥杯省赛C++B组,第七届蓝桥杯省赛JAVAA组)

有 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
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
/*
将每个字符指向他该到达的位置 例如3 1 2 5 4
3->2 1->3 2->1 4->5 5->4
总共是两个环 我们发现答案12345 最终是n个自环 而要让一个环变成若干个自环需要(环的点数-1)
也就是n个字符 要变成n个自环 需要n-环的个数 次
*/
int b[N];
bool st[N];
int n,cnt;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=1;i<=n;i++)
    {
        if(!st[i])
        {
            cnt++;//环的个数+1
            for(int j=i;!st[j];j=b[j])//j指向b[j] 例如3->2
            st[j]=true;
        }
    }
    cout<<n-cnt<<endl;
    return 0;
}

 完全二叉树的权值(第十届蓝桥杯省赛C++A/B组,第十届蓝桥杯省赛JAVAA组)

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

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

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

注:根的深度是 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
#include<iostream>
using namespace std;
typedef long long LL;//一定要开 longlong 我就是没开longlong wa了好几发
const int N = 1e5+10;
int a[N];
int main()
{
    int n;
    cin >> n;
    int ans=0;LL sum=-1e18;//ans表示答案 sum表示当前计入的深度权值和最大值
    int dep=1,cnt=1;//使用dep来表示层数(或者叫做深度) cnt表示当前那一层的节点数
    for(int i=1;i<=n;i++)cin>>a[i];
    
    
    for(int i=1;i<=n;)
    {
        LL temp=0;//临时计入当前层数的深度权值和
        for(int j=i,t=0;t<cnt&&i<=n;j++,i++,t++)//进行遍历 从i开始 遍历cnt次  这边写的比较复杂 懒得优化 其实不需要这么多变量
        {
            temp+=a[j];//进行累加操作 当前的深度权值和
        }
        if(temp>sum){ans=dep; sum=temp;}//如果当前权值和大于 已经记录的最大权值和 那就进行更新 并且将ans等于当前深度dep
        dep++;cnt*=2;//深度加1 二叉树的性质 叶子结点*2
    }
    cout<<ans<<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!
#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
struct Point{
    int x,y,z;
};
int L,R,C;
char g[N][N][N];
int dist[N][N][N];
queue<Point> q;
//定义前后上下左右三个坐标
int dx[6]={1,-1,0,0,0,0};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,1,-1};
int bfs(Point start,Point end)
{//加入起点
    q.push(start);
    memset(dist,-1,sizeof dist);//初始化距离
    dist[start.x][start.y][start.z]=0;//起点到自己距离为0
    while(q.size())
    {
        Point t=q.front();
        //到达终点
        if(t.x==end.x&&t.y==end.y&&t.z==end.z)return dist[t.x][t.y][t.z];
        q.pop();
        for(int i=0;i<6;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i],z=t.z+dz[i];
            if(x<0||x>=L||y<0||y>=R||z<0||z>=C)continue;//越界
            if(g[x][y][z]=='#')continue;//遭遇障碍物
            if(dist[x][y][z]!=-1)continue;//已经搜过
            dist[x][y][z]=dist[t.x][t.y][t.z]+1;//距离+1
            q.push({x,y,z});//加入队列
        }
    }
    return -1;
}
int main()
{
    while(cin>>L>>R>>C,L||R||C)
    {
        Point start,end;
        for(int i=0;i<L;i++)
            for(int j=0;j<R;j++)
                for(int k=0;k<C;k++)
                {
                    cin>>g[i][j][k];
                    //寻找起点和终点
                    if(g[i][j][k]=='S')start={i,j,k};
                    if(g[i][j][k]=='E')end={i,j,k};
                }
                //进行bfs
        int ans=bfs(start,end);
        if(ans==-1)printf("Trapped!\n");
        else printf("Escaped in %d minute(s).\n",ans);
    }
    return 0;
}

 全球变暖(第九届蓝桥杯省赛C++A/B组,第九届蓝桥杯省赛JAVAA/B组)

你有一张某海域 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
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
char g[N][N];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int ans=0,res=0;
void dfs1(int x,int y)
{
    g[x][y]='*';
   for(int k=0;k<4;k++)
   {
       int a=x+dx[k],b=y+dy[k];
       if(g[a][b]=='#'&&a>=0&&a<n&&b>=0&&b<n)dfs1(a,b);
   }
}
void dfs2(int x,int y)
{
    g[x][y]='*';
   for(int k=0;k<4;k++)
   {
       int a=x+dx[k],b=y+dy[k];
       if(g[a][b]=='#'&&a>=0&&a<n&&b>=0&&b<n)dfs2(a,b);
   }
}
int main()
{
    
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>g[i][j];
    
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(g[i][j]=='#')
            {
                dfs1(i,j);
                res++;
            }
        }
    }
    int x=-1,y=-1;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(g[i][j]=='#'){
                dfs2(i,j);
                ans++;
            }
        }
    cout<<res-ans<<endl;
    return 0;
            
}

 大臣的旅费(第四届蓝桥杯省赛C++A组,第四届蓝桥杯省赛JAVAA组)

很久以前,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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5+10;
int n;
struct Edge{
    int id,x;
};
vector<Edge> h[N];
int dist[N];
void dfs(int u,int father,int distance)
{
    dist[u]=distance;
    for(auto node:h[u])
        if(node.id!=father)
            dfs(node.id,u,distance+node.x);
}
int main()
{
    cin>>n;
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        h[a].push_back({b,c});
        h[b].push_back({a,c});
    }
    dfs(1,-1,0);
    int u=1;
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[u])
            u=i;
    
    memset(dist, 0, sizeof dist);
    dfs(u,-1,0);
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[u])
            u=i;
            
    int s=dist[u];
    cout<<s*10+(s+1ll)*s/2<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值