面试笔试算法-搜索综合问题

Oj81:小明回家

题目描述

小明看完了电影,是时候回家了,可是这时他突然得知小米之家的小米9现货开卖了,这款手机小明已经想了一个多月,知道这个消息后的他十分兴奋,一定要在回家之前先去小米之家买手机(城市中有一个或多个小米之家),请计算小明从电影院到任意一个小米之家买手机后回家的最短距离(只能朝上下左右四个方向行走,除了障碍物外,其他地方都可以通过),数据保证可以买完手机后回家。


输入

第 1 行两个数 n 和 m 表示地图有 n 行 m 列 2≤n,m≤2000 第 2 行至第 n+1 行为地图 其中 S 表示电影院 T 表示家 P 表示小米之家 . 为可以通过的点 # 为障碍物

输出

一个整数 表示小明从电影院去小米之家再回家的总距离


样例输入
5 5
.S...
###..
....T
.P##.
P....
样例输出
11

数据规模与约定

时间限制:5 s

内存限制:256 M

100% 的数据保证 2≤n,m≤2000

思路:

1、存储状态
2、起始状态
3、终止状态
4、状态转移
5、去重

在这里插入图片描述

#include<iostream>
#include <queue>
using namespace std;

// f表示是否去过手机店
struct node {
    int x, y, step, f;
};
// 去重数组check:
/*0:两种状态都没去过
  1:没手机的时候去过
  2:有手机的时候去过
  3:有手机和没手机都去过
*/ 
int n, m, check[2005][2005];
char mmap[2005][2005];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

int main(int argc, char *grav[])
{
    cin >> n >> m;
    queue<node> que;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == 'S') {
                que.push((node){i, j, 0, 0});
                check[i][j] = 1;
            }
        }
    }
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            // 没手机的时候是否去过
            if ((check[x][y] & 1) && temp.f == 0) {
                continue;
            }
            // 有手机的时候是否去过
            if ((check[x][y] & 2) && temp.f == 1) {
                continue;
            }
            if (mmap[x][y] == 'T' && temp.f == 1) {
                cout << temp.step + 1 << endl;
                return 0;
            }
            if (mmap[x][y] == '.' || mmap[x][y] == 'S' || mmap[x][y] == 'T') {
                que.push((node){x, y, temp.step + 1, temp.f});
                check[x][y] += temp.f + 1;
            }
            if (mmap[x][y] == 'P') {
                que.push((node){x, y, temp.step + 1, 1});
                check[x][y] = 3;
            }
        }
    }
    cout << -1 << endl;
    return 0;
}

Oj528:关系网络

题目描述

有 n 个人,编号为 1∼n。其中有一些人互相认识,现在 x 想要认识 y,可以通过他所认识的人来认识更多的人(如果 a,b 相识,a,c 相识,那么 b 可以通过 a 来认识 c),求 x 最少需要通过多少中间人才能认识 y。


输入

第一行三个整数 n,x,y。(1≤x,y≤n≤100)

接下来是一个 n∗n 的邻接矩阵,a[i,j]=1 则说明 i,j 相识,=0 则不相识,保证 a[i,j]=a[j,i] 且 a[i,i]=0。

输出

输出一个数,表示最少人数,若无法相识,输出 0。


样例输入
5 1 5
0 1 0 0 0
1 0 1 1 0
0 1 0 1 0
0 1 1 0 1
0 0 0 1 0
样例输出
2

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤x,y≤n≤100

思路:

在这里插入图片描述

代码演示:
#include<iostream>
#include <queue>
using namespace std;
struct node {
    int now, step;
};
int n, x, y, check[105], arr[105][105];
int main(int argc, char *grav[])
{
    cin >> n >> x >> y;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> arr[i][j];
        }
    }
    queue<node> que;
    que.push((node){x, 0});
    check[x] = 1;
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 1; i <= n; i++) {
            if (arr[temp.now][i] == 1 && check[i] == 0) {
                if (i == y) {
                    cout << temp.step << endl;
                    return 0;
                }
                que.push((node){i, temp.step + 1});
                check[i] = 1;
            }
        }
    }
    cout << 0 << endl;
    return 0;
}

Oj538:图的遍历

题目描述

读入一个用邻接矩阵存储的无向图,输出它的深度优先遍历序列。(以 1 为起点,按照编号越小权值越大的规则)


输入

第一行一个正整数 N,表示节点个数。(5≤N≤20)

接下来输入一个邻接矩阵,a[i,j]=0 表示 i,j之间不存在边,=1 说明存在边。

输出

格式参照样例。


样例输入1
8
0 1 1 0 0 0 0 0 
1 0 0 1 1 0 0 0 
1 0 0 0 0 0 1 1 
0 1 0 0 0 1 0 0 
0 1 0 0 0 1 0 0 
0 0 0 1 1 0 0 0 
0 0 1 0 0 0 0 1 
0 0 1 0 0 0 1 0
样例输出1
1-2-4-6-5-3-7-8
样例输入2
5
0 0 0 0 0 
0 0 0 1 0 
0 0 0 1 0 
0 1 1 0 0 
0 0 0 0 0
样例输出2
1-2-4-3-5

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 5≤N≤20

代码演示:
#include<iostream>
using namespace std;

int n, arr[25][25], check[25];

void func(int now) {
    if (now != 1)  {
        cout << "-";
    }
    cout << now;
    check[now] = 1;
    for (int i = 1; i <= n; i++) {
        if (check[i] == 0 && arr[now][i] == 1) {
            func(i);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> arr[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        if (check[i] == 0) {
            func(i);
        }
    }
    cout << endl;
    return 0;
}

Oj402:奇怪的电梯

题目描述

一栋有 N 层的大楼中有一部奇怪的电梯,每层只有上下两个钮,且第 i 层只能一次性上或下 Xi 层(若下 Xi 层后,层数小于 1 ,则不可进行下楼操作,同理有些层也不可进行上楼操作),小明现在在 A 层,他想去 B 层,求他需要的最少操作次数,若无法到达,则输出 −1。


输入

第一行输入三个整数N,A,B。(1≤A,B≤N≤200)

接下来 N 行,每行一个数表示 Xi。(1≤Xi≤N)

输出

输出最小操作次数,若无法抵达则输出 −1。


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

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤A,B≤N≤200

思路:

now:记录当前的位置,step:走了多少步

在这里插入图片描述

代码演示:
#include<iostream>
#include <queue>
using namespace std;

struct node {
    int now, step;
};
int n, a, b, num[205], check[205];

int main(int argc, char *grav[])
{
    cin >> n >> a >> b;
    for (int i = 1; i <= n; i++) {
        cin >> num[i];
    }
    queue<node> que;
    que.push((node){a, 0});
    check[a] = 1;
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        int up = temp.now + num[temp.now];
        int down = temp.now - num[temp.now];
        if (up == b || down == b) {
            cout << temp.step + 1 << endl;
            return 0;
        }
        if (up <= n && check[up] == 0) {
            que.push((node){up, temp.step + 1});
            check[up] = 1;
        }
        if (down >= 1 && check[down] == 0) {
            que.push((node){down, temp.step + 1});
            check[down] = 1;
        }
    }
    cout << -1 << endl;
    return 0;
}

Oj530:警察找车

题目描述

年轻的拉尔夫开玩笑地从一个小镇上偷走一辆车,但他没想到的是那车属于警察局。并且车上装有用于发射车子移动路线的装置。那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息。

通过使用一张小镇的地图,帮助警察局找到那车。表示出该车最终所有可能的位置。

小镇的地图是矩形的,上面的符号用来标明那儿可以行车和那儿不行。“.” 表示小镇上那块地方是可以行车的,符号 “X” 表示此处不能行车。拉尔夫所开小车的初始位置,用字符的 “∗”,表示,且汽车能从初始位置通过。汽车能向四个方向移动:向北(上),南(下),西(左),东(右);拉尔夫所开小车的行动路线是通过一组给定的方向来描述的,在每个给定的方向,拉尔夫驾驶小车通过小镇上的一个或更多的可行车地点。


输入

第一行两个整数 n,m,表示小镇地图大小。(1≤n,m≤50)

接下来是一个 n∗m 的矩阵,由 .∗X 组成,表示小镇的地图。

再接下来一行一个整数 N(1≤N≤1000),表示接下来有 N 个方向。

接下来 N 行,每行一个方向单词,NORTH,SOUTH,WEST,EAST 分别表示上下左右,代表汽车的移动方向,任何两个连续的方向都不相同。

输出

输出小镇的地图,在地图上,汽车最终可能出现的点用 ∗ 来表示。


样例输入
4  5
.....
.X...
...*X
X.X..
3
NORTH
WEST
SOUTH
样例输出
.....
*X*..
*.*.X
X.X..

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤n,m≤50

思路:

在这里插入图片描述

代码演示:
#include<iostream>
#include <queue>
#include <string>
using namespace std;

struct node {
    int x, y;
};
int n, m, cnt;
int dir[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1};
char mmap[55][55];
int main(int argc, char *grav[])
{
    queue<node> que;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == '*') {
                que.push((node){i, j});
                mmap[i][j] = '.'; // 起点也是普通可以走车的点,故标记为'.'
            }
        }
    }
    cin >> cnt;
    while (cnt--) {
        string str;
        cin >> str;
        int dir_num, check[55][55] = {0}, qsize = que.size();
        if (str == "NORTH") {
            dir_num = 0;
        } else if (str == "SOUTH") {
            dir_num = 1;
        } else if (str == "WEST") {
            dir_num = 2;
        } else {
            dir_num = 3;
        }
        while (qsize--) {
            node temp = que.front();
            que.pop();
            for (int i = 1; 1; i++) {
                int x = temp.x + dir[dir_num][0] * i;
                int y = temp.y + dir[dir_num][1] * i;
                if (mmap[x][y] != '.') {
                    break;
                }
                if (check[x][y] == 0) {
                    que.push((node){x, y});
                    check[x][y] = 1;
                }
            }
        }
    }
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        mmap[temp.x][temp.y] = '*';
    }
    for (int i = 1; i <= n; i++) {
        cout << &mmap[i][1] << endl;
    }
    return 0;
}

Oj531:奇怪的电视

题目描述

小明过年的时侯去如姥姥家,除夕之夜,大家都想看春节联欢晚会,而可以依赖的就是一台旧电视。

那一台旧电视不是遥控器控制的,上面有许多按钮,按下某一按钮,其他按钮都将被释放,只有被按的按钮工作(如果其他按钮本来就是释放的状态,那么它们保持不变,处于按下状态的按钮不能重复按下,这对下文依旧适用)。当小明到来的那一天,上面的许多按钮突然无法正常工作。现在按下某个按钮后,有一些按钮将被释放,而另外的一些按钮将不改变原状态。经过一番惨无人道的折腾,小明知道按下每一个按钮会产生什么样的效果。现在他只需要第3个按钮正常工作。

现在帮助小明计算,从给定的状态到只有按钮3工作,而其他按钮都被释放这个最终状态所需按下的按钮序列的最短长度。


输入

第一行一个整数 N,表示按钮数。(3≤N≤20)

第二行包含 N 个数,表示各按钮的初始状态。0 表示相应的按钮是释放的,1 表示相应的按钮是按下的。

接下来的 N 行,表示按下某个按钮时将有那些按钮被释放。第 M+2 行由数字 K 开头,紧跟着 K 个数字(按升序排列),表示当按下按钮 M 时被释放的按钮数及按钮号码(按钮号码用数字 1∼M 表示)。每个按钮不能释放其本身,也可能不释放任何按钮。

输出

输出一个数,表示从给定的状态到只有按钮3工作而其他按钮都被释放这个最终状态所需按下的按钮序列的最短长度。


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

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 3≤N≤20

思路:

1、二进制位来存储状态

在这里插入图片描述

代码演示:
#include<iostream>
#include <queue>
using namespace std;

struct node {
    int status, step;
};
// num2:二进制的值,num:按下i按钮后有哪些按键弹出
int n, num2[22] = {1}, check[2200000], num[25];

void init() {
    for (int i = 1; i <= 20; i++) {
        num2[i] = num2[i - 1] * 2;
    }
}

int main(int argc, char *grav[])
{
    init();
    cin >> n;
    int start = 0;
    for (int i = 1; i <= n; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            start += num2[i];
        }
    }
    for (int i = 1; i <= n; i++) {
        int t;
        cin >> t;
        for (int j = 1; j <= t; j++) {
            int t2;
            cin >> t2;
            num[i] += num2[t2];
        }
    }
    queue<node> que;
    que.push((node){start, 0});
    check[start] = 1;
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        if (temp.status == 8) {
            cout << temp.step << endl;
            return 0;
        }
        for (int i = 1; i <= n; i++) {
            if ((temp.status & num2[i]) == 0) {
                int t = temp.status;
                t &= ~num[i];
                t |= num2[i];
                if (check[t] == 0) {
                    que.push((node){t, temp.step + 1});
                    check[t] = 1;
                }
            }
        }
    }
    cout << -1 << endl;
    return 0;
}

Oj537:门票问题

题目描述

有很多人在门口排队,每个人将会被发到一个有效的通行密码作为门票。一个有效的密码由 L 个小写字母组成,至少有一个元音 (a,e,i,o,u) 和两个辅音,并且是按字母表顺序出现的,例如 abc 是有效的,而 cba 不是。

现在给定一个期望长度 L 和 C 个小写字母,输出所有有效密码。


输入

第一行两个正整数 L,C。(3≤L≤15,C≤26)

接下来一行输入 C 个小写字母。

输出

按照字母表顺序输出所有密码,一行一个,若密码超过 25000 时,只输出前 25000个密码。


样例输入
4 6
a t c i s w
样例输出
acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw

数据规模与约定

时间限制:1 s
内存限制:256 M
100% 的数据保证 3≤L≤15,C≤26

代码实现
#include<iostream>
#include <algorithm>
using namespace std;
int l, c, cnt, ynum, fnum;
char num[30], ans[30];

void p() {
    for (int i = 0; i < l; i++) {
        cout << ans[i];
    }
    cout << endl;
}

int func(int s, int left, int deep) {
    if (deep == l) {
        if (ynum >= 1 && fnum >= 2) {
            p();
            cnt++;
            if (cnt == 25000) {
                return 1;
            }
        }
        return 0;
    }
    for (int i = s; i < c; i++) {
        ans[deep] = num[i];
        int f = 0;
        if (num[i] == 'a' || num[i] == 'e' || num[i] == 'i' || num[i] == 'o' || num[i] == 'u') {
            ynum++;
            f = 1;
        } else {
            fnum++;
        }
        if (func(i + 1, left - 1, deep + 1) == 1) {
            return 1;
        }
        if (f == 0) {
            fnum--;
        } else {
            ynum--;
        }
    }
    return 0;
}

int main(int argc, char *grav[])
{
    cin >> l >> c;
    for (int i = 0; i < c; i++) {
        cin >> num[i];
    }
    sort(num, num + c);
    func(0, l, 0);
    return 0;
}

Oj541:相遇问题

题目描述

贝丽斯和她的姐姐艾丽斯想从谷仓走到她们最喜爱的牧场。她们在同一时间离开谷仓,也在同一时间到达最喜爱的牧场。

整个农场共有 N 个牧场,1 号牧场就是谷仓,N 号牧场是她们最喜爱的牧场。整个农场是建在一个山坡上的,如果 X<Y,则代表 X 号牧场比 Y 牧场要高。有 M 条路径连接一堆牧场。然而,由于每条路径都很陡,每条路只能向下山的方向走。比如,一条连接 5 号和 8 号农场的路只能从 5 走到 8 而不能反过来,因为那样就是向山上走了。每对牧场之间最多有一条路径,故 M≤N(N−1)/2。

贝丽斯和艾丽斯可能需要不同的时间来走过一条路径。例如,贝丽斯可能花 10 个单位的时间,而艾丽斯会花 20 个单位,而且她们只在路径上花时间,在牧场上是不花时间的。

请帮助决定贝丽斯和艾丽斯至少要花多少时间,使她们能同时到达她们最喜爱的农场。


输入

第一行两个整数 N,M。

接下来的 M 行,每行有四个整数 A,B,C,D,表示 A 牧场和 B 牧场是被连接的,C 是贝丽斯经过这条路要花的时间,D 是艾丽斯经过这条路要花的时间。C 和 D 的范围是 1∼1000。

输出

输出一行一个整数,表示贝丽斯和艾丽斯至少要花多少时间使她们能同时到达她们最喜爱的农场。如果这是不可能的,或者根本就没有路径使她们能到达她们最喜爱的农场,在一行输出 IMPOSSIBLE。


样例输入
3 3
1 3 1 2
1 2 1 2
2 3 1 2
样例输出
2
样例说明

贝丽斯在每条路径上都比艾丽斯走得速度快 1 倍;但是如果贝丽斯采用路径 1,2,3,艾丽斯采用路径 1,3,那么她们将在同一时间到达。


数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤N≤16

代码实现
#include<iostream>
using namespace std;

int n, m, arr[2][20][20], ans[2][100000], cnt[2];

// pnum:第几个人,now:现在在哪个点,cost:现在消耗的时间
void func(int pnum, int now, int cost) {
    if (now == n) {
        ans[pnum][cnt[pnum]++] = cost;
        return;
    }
    for (int i = now + 1; i <= n; i++) {
        if (arr[pnum][now][i] != 0) {
            func(pnum, i, cost + arr[pnum][now][i]);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        arr[0][a][b] = arr[0][b][a] = c;
        arr[1][a][b] = arr[1][b][a] = d;
    }
    func(0, 1, 0);
    func(1, 1, 0);
    int fin = 210000000;
    for (int i = 0; i < cnt[0]; i++) {
        if (fin < ans[0][i]) {
            continue;
        }
        for (int j = 0; j < cnt[i]; j++) {
            if (ans[0][i] == ans[1][j]) {
                fin = ans[0][i];
            }
        }
    }
    if (fin == 210000000) {
        cout << "IMPOSSIBLE" << endl;
    } else {
        cout << fin << endl;
    }
    return 0;
}

总结:

欧拉计划:

滑动窗口法

方向数组怎么用

记忆数组的使用

什么叫记忆化?

大整数

最简单的动态规划

二分专题:

sort怎么用

朴素二分

二分查找特殊情况:

二分答案

做题周:

leetcode题 2sum

Oj题:模拟/贪心/枚举

STL:

容器:queue、stack、priority_queue、string、vector、map

map–>m -->unmap

六大组件:

搜索:

递归、排列组合、走地图、二进制按位存储状态

存、起点、终端、转移、去重

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值