算法刷题day08

目录

  • 引言
  • 一、完全二叉树的权值
  • 二、地牢大师
  • 三、全球变暖
  • 四、大臣的旅费
  • 五、总结

引言

今天的刷题刷的是双指针跟BFS,其中包括了图的BFS,以及怎么去建图的问题,还是慢慢练,慢慢就熟悉了,然后还有一些竞赛中要用到的数学函数还是得记记多多积累!

一、完全二叉树的权值

标签:双指针算法

思路:就是规定一个起点,然后再走固定的个数,再一判断就行了,再把个数左移一位

题目描述:

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

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

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

注:根的深度是 1。

输入格式
第一行包含一个整数 N。

第二行包含 N
 个整数 A1,A2,⋅⋅⋅AN。

输出格式
输出一个整数代表答案。

数据范围
1≤N≤105,105≤Ai≤105
输入样例:
7
1 6 5 4 3 2 1
输出样例:
2

示例代码:

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5+10;

int a[N];
int n;

int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
    
    LL res = -1e18;
    int id = 0;
    for(int i = 0, j = 1; i < n;)
    {
        LL step = 0;
        int t = i;
        while(i < t + j && i < n)  //完全二叉树 i可能会越界
        {
            step += a[i];
            i++;
        }
        //cout << step << endl;
        
        if(step > res)
        {
            res = step;
            id = log2(j);
        }
        j <<= 1;
    }
    
    //cout << res << endl;
    cout << id+1 << endl;
    
    return 0;
}

二、地牢大师

标签:BFS

思路:就是一个BFS模板,然后区别是三维的,其实只不过方向多了两,其余都差不多

题目描述:

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

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

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

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

请问,你有可能逃脱吗?

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

输入格式
输入包含多组测试数据。

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

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

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

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

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

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

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

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

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

数据范围
1≤L,R,C≤100
输入样例:
3 4 5
S....
.###.
.##..
###.#

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

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

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

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

示例代码:

#include 
#include 
#include 
#include 

using namespace std;

const int N = 110;


struct Node
{
    int x,y,z;
};

int l,r,c;
char g[N][N][N];
int dist[N][N][N];
Node S, E;
Node q[N*N*N];

int dir[6][3] = {0,0,1, 0,0,-1, 0,1,0, 0,-1,0, 1,0,0, -1,0,0};

int bfs()
{
    memset(dist, -1, sizeof dist);
    dist[S.x][S.y][S.z] = 0;
    
    int hh = 0, tt = -1;
    q[++tt] = {S.x,S.y,S.z};
    while(hh <= tt)
    {
        auto t = q[hh++];
        for(int i = 0; i < 6; ++i)
        {
            int x = t.x + dir[i][0];
            int y = t.y + dir[i][1];
            int z = t.z + dir[i][2];
            
            if(x < 0 || x >= l || y < 0 || y >= r || z < 0 || z >= c) continue;
            if(dist[x][y][z] != -1 || g[x][y][z] == '#') continue;
            
            dist[x][y][z] = dist[t.x][t.y][t.z] + 1;
            q[++tt] = {x,y,z};
            
            if(g[x][y][z] == 'E') return dist[x][y][z];
        }
    }
    
    return -1;
}

int main()
{
    while(1)
    {
        scanf("%d%d%d", &l, &r, &c);
        if(l == 0 || r == 0 || c == 0) break;
        
        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') S = {i,j,k};
                    if(g[i][j][k] == 'E') E = {i,j,k};
                }
            }
        }
        
        int t = bfs();
        
        if(t == -1) puts("Trapped!");
        else printf("Escaped in %d minute(s).\n", t);
    }
    
    return 0;
}

三、全球变暖

标签:BFS

思路:一种是先把岛屿标记一下,然后遍历每个岛屿,如果这个岛屿中存在一个点四个方向都为’#',那么res++。第二种就是BFS的时候计算点的数量,以及淹没后点的数量,如果相等说明完全淹没。

题目描述:

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

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

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

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

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

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

输入格式
第一行包含一个整数N。

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

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

输出格式
一个整数表示答案。

数据范围
1≤N≤1000
输入样例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出样例1:
1
输入样例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
输出样例2:
1

示例代码:

思路一:先把岛屿找出来,然后遍历岛屿,看岛屿中是否存在一个点上下左右都是#

#include 
#include 
#include 

#define x first
#define y second

using namespace std;

const int N = 1010;

typedef pair<int,int> PII;


int n;
char g[N][N];
bool st[N][N], backup[N][N];
PII q[N*N];

int dir[4][2] = {0,1,0,-1,1,0,-1,0};

bool bfs1(int sx, int sy)
{
    st[sx][sy] = true;
    
    int hh = 0, tt = -1;
    q[++tt] = {sx,sy};
    while(hh <= tt)
    {
        auto t = q[hh++];
        for(int i = 0; i < 4; ++i)
        {
            int x = t.x + dir[i][0];
            int y = t.y + dir[i][1];
            
            if(x < 0 || x >= n || y < 0 || y >= n) continue;
            if(st[x][y] || g[x][y] == '.') continue;
            
            st[x][y] = true;
            q[++tt] = {x,y};
        }
    }
    
    return true;
}

bool bfs2(int sx, int sy)
{
    //memcpy(backup, st, sizeof st);
    bool flag = true;
    st[sx][sy] = false;
    
    int hh = 0, tt = -1;
    q[++tt] = {sx,sy};
    while(hh <= tt)
    {
        auto t = q[hh++];
        for(int i = 0; i < 4; ++i)
        {
            int x = t.x + dir[i][0];
            int y = t.y + dir[i][1];
            
            if(x < 0 || x >= n || y < 0 || y >= n) continue;
            if(!st[x][y]) continue;
            if(g[x][y] == '.') continue;
            
            if(g[x-1][y] == '#' && g[x+1][y] == '#' && g[x][y-1] == '#' && g[x][y+1] == '#') flag = false;;
            
            st[x][y] = false;
            q[++tt] = {x,y};
        }
    }
    
   // memcpy(st,backup, sizeof st);
    
    return flag;
}


int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; ++i) scanf("%s", g[i]);
    
    int res1 = 0, res2 = 0;  // 代表岛屿数量
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            if(g[i][j] == '#' && !st[i][j])
            {
                bool t = bfs1(i,j);
                res1++;
            }
        }
    }
    
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            if(st[i][j])
            {
                if(bfs2(i,j))
                {
                    res2++;
                }
            }
        }
    }
    //cout << res1 << " " << res2 << endl;
    cout << res2 << endl;
    
    return 0;
}

思路二:通过BFS判断每个岛屿中的岛屿数顺便判断海洋数

#include 
#include 
#include 

using namespace std;

typedef pair<int,int> PII;
#define x first
#define y second

const int N = 1010;

int n;
char g[N][N];
bool st[N][N];
PII q[N*N];

int dir[4][2] = {0,1,0,-1,1,0,-1,0};

bool bfs(PII start)
{
    st[start.x][start.y] = true;
    
    int total = 1, sea = 0;
    int hh = 0, tt = -1;
    q[++tt] = {start.x,start.y};
    while(hh <= tt)
    {
        auto t = q[hh++];
        bool flag = false;
        for(int i = 0; i < 4; ++i)
        {
            int x = t.x + dir[i][0];
            int y = t.y + dir[i][1];
            
            if(x < 0 || x >= n || y < 0 || y >= n) continue;
            if(st[x][y]) continue;
            if(g[x][y] == '.') {flag = true; continue;}
            
            st[x][y] = true;
            total++;
            q[++tt] = {x,y};
        }
        if(flag) sea++;
    }
    
    //cout << sea << " " << total << endl;
    if(sea == total) return true;
    else return false;
}

int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; ++i) scanf("%s", g[i]);
    
    int res = 0;
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            if(!st[i][j] && g[i][j] == '#')
            {
                if(bfs({i,j})) res++;
            }
        }
    }
    
    printf("%d\n", res);
    
    return 0;
}

四、大臣的旅费

标签:树的直径,BFS

思路:本题其实就是求树的直径:任取一点x,BFS一下,找距离最远的点,然后再从这个点为起点,再遍历一次,这时最远的距离就是树的直径了

题目描述:

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

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

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

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

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

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

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

聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。

具体来说,一段连续的旅途里,第 1 千米的花费为 11,第 2 千米的花费为 12,第 3 千米的花费为 13,…,第 x 千米的花费为 x+10。

也就是说,如果一段旅途的总长度为 1 千米,则刚好需要花费 11,如果一段旅途的总长度为 2 千米,则第 1 千米花费 11,
第 2 千米花费 12,一共需要花费 11+12=23。

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

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

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

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

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

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

数据范围
1≤n≤105,1≤Pi,Qi≤n,1≤Di≤1000
输入样例:
5
1 2 2
1 3 1
2 4 5
2 5 4
输出样例:
135

示例代码:

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5+10, M = N * 2;

int h[N], e[M], w[M], ne[M], idx;
int n;
LL dist[N];  //到i号点的距离
int q[N];

LL maxid, maxs = -1e18;

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void bfs(int start)
{
    memset(dist, -1, sizeof dist);
    dist[start] = 0;
    
    int hh = 0, tt = -1;
    q[++tt] = start;
    while(hh <= tt)
    {
        auto t = q[hh++];
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] != -1) continue;
            
            dist[j] = dist[t] + w[i];
            q[++tt] = j;
            
            if(dist[j] > maxs) {maxs = dist[j], maxid = j;}
        }
    }
}

int main()
{
    memset(h, -1, sizeof h);
    
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a,b,c);
        add(b,a,c);
    }
    
    bfs(1);
    
    maxs = -1e18;
    bfs(maxid);
    
    LL res = maxs * 10 + maxs*(maxs+1)/2;
    
    printf("%lld\n", res);
    
    return 0;
}

五、总结

  • 在BFS时,一般最后return dist,如果在循环里的最后return,那么起点即终点就无法判断了,或者在循环开头写也行,写的时候能够注意就行了

你可能感兴趣的:(#,算法刷题,算法,图论,数据结构)