当前位置: 首页 > news >正文

机试准备第16天

今天进入搜索的学习。搜索问题一般较为复杂,状态空间很大,使用规划过的遍历/枚举,不重复或不遗漏的找到解决问题。有广度优先搜索以及深度优先搜索。广度优先搜索访问某节点时,将其邻居置为待访问节点。广度优先遍历一般用于寻找最优解。层序遍历本质上就是广度优先遍历,BFS的核心数据结构是队列。深度优先遍历是一条路走到黑,实现DFS就是从大问题到小问题,知道最小问题,用递归或分治法实现。

第一题是树的高度。把这棵树看成图的特例,从根出发使用BFS遍历。

#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;
int main(){
    int n,m;
    scanf("%d%d", &n,&m);//编号是1,2,3....n
    vector<vector<int>> tree(n+1);//tree[0]没用
    //tree[1]~tree[n] 对应不同编号的节点
    for(int i = 0;i<n-1;i++){
        int u, v;
        scanf("%d%d", &u, &v);
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    queue<int> Tovisit;//用来做BFS的队列
    vector<int> distance(n+1);//用来判断某个节点是否访问过
    //记录根节点到这个节点的最短距离
    for(int i = 1;i<=n;i++){
        distance[i] = -1;
    }
    distance[m] = 0;//根节点到根节点的距离是0
    int maxdis = 0;
    Tovisit.push(m);
    while(!Tovisit.empty()){
        int cur = Tovisit.front();
        Tovisit.pop();
        
        for(int i = 0;i<tree[cur].size();i++){
            int child = tree[cur][i];
            if(distance[child]!=-1){
                continue;
            }
            Tovisit.push(child);
            distance[child] = distance[cur]+1;
            maxdis = distance[child];
        }
    }
    printf("%d", maxdis);

    return 0;
}

第二题是玛雅人的密码。使用map结构避免重复。

#include <stdio.h>
#include <string>
#include <queue>
#include <unordered_map>//时间复杂度O(1)
using namespace std;
int main(){
    char strarr[20] = {0};
    int n;
    scanf("%d", &n);
    scanf("%s", strarr);
    if(n < 4) {
        printf("-1");
        return 0;
    }
    string str = strarr;
    queue<string> myqueue;
    myqueue.push(str);
    unordered_map<string , int> dismap;
    dismap.insert({str, 0});
    while(!myqueue.empty()){
        string cur = myqueue.front();
        if(cur.find("2012")!=string::npos){
            printf("%d\n", dismap[cur]);
            break;
        }
        myqueue.pop();
        
        for(int i = 0;i<cur.size()-1;i++){
            string next = cur;
            char temp = next[i];
            next[i] = next[i+1];
            next[i+1] = temp;
            
            if(dismap.count(next) == 0){
                myqueue.push(next);
                dismap.insert({next, dismap[cur]+1});
            }
        }
        
        
    }
    
    if(myqueue.empty()) printf("-1\n");
}

第三题是走路还是走公交,使用map指示当前pos以及到达pos的最小时间。太多map.count导致超时了。

#include <stdio.h>
#include <queue>
#include <unordered_map>
using namespace std;
int main(){
    int n,k;
    while(scanf("%d%d", &n, &k)!= EOF){
        queue<int> pos;//位置队列
        pos.push(n);
        unordered_map<int, int> res;//位置 时间
        res.insert({n, 0});
        while(!pos.empty()){
            int curpos = pos.front();//当前位置
            pos.pop();
            if(curpos == k){
                printf("%d\n", res[curpos]);
                break;
            }
            else{
                if(res.count(curpos+1) == 0&&(curpos+1)<=100000&&curpos<k){
                res.insert({curpos+1, res[curpos]+1});
                pos.push(curpos+1);
                }
                if(res.count(curpos-1) == 0&&(curpos-1)>=0){
                res.insert({curpos-1, res[curpos]+1});
                pos.push(curpos-1);
                }
                if(res.count(curpos*2) == 0&&curpos*2<=100000&&curpos<k){
                res.insert({curpos*2, res[curpos]+1});
                pos.push(curpos*2);
                }
            }
            
            
        }
    }
    return 0;
}
#include <stdio.h>
#include <queue>
#include <unordered_map>
using namespace std;

const int MAX = 100000; 

// 我们可以尝试用数组去替代unordered_map
bool visited[MAX + 1]; // 使用数组来记录位置是否被访问过,下标表示位置,值表示是否访问,初始化为false
int steps[MAX + 1];    // 使用数组来记录到达每个位置的步数,初始化为0

int main() {
    int n, k;
    while (scanf("%d%d", &n, &k) != EOF) {
        queue<int> pos;
        pos.push(n);
        visited[n] = true; // 标记起始位置已访问
        steps[n] = 0;      // 起始位置步数为0

        while (!pos.empty()) {
            int curpos = pos.front();
            pos.pop();

            if (curpos == k) {
                printf("%d\n", steps[curpos]);
                break;
            }
            else {
                // 当前位置+1是否在范围内且未被访问过
                if (curpos + 1 <= MAX &&!visited[curpos + 1]) { 
                    visited[curpos + 1] = true;
                    steps[curpos + 1] = steps[curpos] + 1;
                    pos.push(curpos + 1);
                }
                // 当前位置-1是否在范围内且未被访问过
                if (curpos - 1 >= 0 &&!visited[curpos - 1]) { 
                    visited[curpos - 1] = true;
                    steps[curpos - 1] = steps[curpos] + 1;
                    pos.push(curpos - 1);
                }
                // 当前位置*2是否在范围内、未被访问过且乘2操作合理(避免超过目标位置且浪费计算)
                if (curpos * 2 <= MAX &&!visited[curpos * 2] && curpos <= k) { 
                    visited[curpos * 2] = true;
                    steps[curpos * 2] = steps[curpos] + 1;
                    pos.push(curpos * 2);
                }
            }
        }
        // 重置数组状态
        for (int i = 0; i <= MAX; ++i) { 
            visited[i] = false;
            steps[i] = 0;
        }
    }
    return 0;
}

第四题是逃离迷宫。给了,这题是真不会。

#define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <limits.h>
    #include <queue>
    using namespace std;
    struct Node {
        int x;
        int y;
        int direct; // 1,2,3,4 四个方向
        int turn; // 已经转弯的次数
        Node(int _x, int _y, int _direct, int _turn) {
            x = _x;
            y = _y;
            direct = _direct;
            turn = _turn; 
        }
    };
    
    bool operator<(Node lsh, Node rsh) {
        return lsh.turn > rsh.turn;
    }
    
    char maze[200][200];
    
    int main() {
        int t;
        scanf("%d", &t);
    
        for (int idx = 0; idx < t; ++idx) {
            int m, n;
            scanf("%d%d", &m, &n);
            // m n 是行和列
            for (int i = 0; i < m; ++i) {
                scanf("%s", maze[i]);
            }
            int k, x1, x2, y1, y2;
            // k 最多转弯数 
            scanf("%d%d%d%d%d", &k, &y1, &x1, &y2, &x2);
            --x1; // 将编号从1到N 转成下标从0到N-1
            --x2;
            --y1;
            --y2;
            int vis[101][101][5];
            for (int i = 0; i < 101; ++i) {
                for (int j = 0; j < 101; ++j) {
                    vis[i][j][1] = INT_MAX; //目前按照1方向到达i,j位置所经历的最小转弯次数
                    vis[i][j][2] = INT_MAX; //目前按照2方向到达i,j位置所经历的最小转弯次数
                    vis[i][j][3] = INT_MAX; //目前按照3方向到达i,j位置所经历的最小转弯次数
                    vis[i][j][4] = INT_MAX; //目前按照4方向到达i,j位置所经历的最小转弯次数
                }
            }
            priority_queue<Node> tovisit; // 使用优先队列保存待访问的邻居结点
            Node root(x1, y1, 0, 0); // 0 表示当前没有任何方向
            tovisit.push(root);
            bool isfind = false;
            while (!tovisit.empty()) {
                // 当前结点
                Node cur = tovisit.top();
                int x = cur.x, y = cur.y;
                tovisit.pop();
                if (cur.x == x2 && cur.y == y2) {
                    isfind = true;
                    break;
                }
                // 假设下一步走1方向
                int nextTurn = (cur.direct == 1) ? cur.turn : cur.turn + 1;
                // 假设下一个位置在范围内,且消耗转向次数更少,则入队
                if (nextTurn <= k + 1 && x + 1 < m && maze[x + 1][y] != '*' && vis[x + 1][y][1] > nextTurn) {
                    Node next(x + 1, y, 1, nextTurn);
                    tovisit.push(next);
                    vis[x + 1][y][1] = nextTurn;
                }
    
                // 假设下一步走2方向
                nextTurn = (cur.direct == 2) ? cur.turn : cur.turn + 1;
                if (nextTurn <= k + 1 && x - 1 >= 0 && maze[x - 1][y] != '*' && vis[x - 1][y][2] > nextTurn) {
                    Node next(x - 1, y, 2, nextTurn);
                    tovisit.push(next);
                    vis[x - 1][y][2] = nextTurn;
                }
                nextTurn = (cur.direct == 3) ? cur.turn : cur.turn + 1;
                if (nextTurn <= k + 1 && y + 1 < n && maze[x][y + 1] != '*' && vis[x][y + 1][3] > nextTurn) {
                    Node next(x, y + 1, 3, nextTurn);
                    tovisit.push(next);
                    vis[x][y + 1][3] = nextTurn;
                }
                nextTurn = (cur.direct == 4) ? cur.turn : cur.turn + 1;
                if (nextTurn <= k + 1 && y - 1 >= 0 && maze[x][y - 1] != '*' && vis[x][y - 1][4] > nextTurn) {
                    Node next(x, y - 1, 4, nextTurn);
                    tovisit.push(next);
                    vis[x][y - 1][4] = nextTurn;
                }
            }
            if (isfind) {
                printf("yes\n");
                // printf("%d\n", dis[x2][y2]);
            }
            else {
                printf("no\n");
            }
    
        }
        return 0;
    }

第五题是八皇后。经典递归牢题。

#include <stdio.h>
#include <vector>
using namespace std;
//希望所有的函数都能访问queenvec
vector<vector<int>> queenvec;//所有的合法皇后序列
void DFSFindQueen(vector<int> &queen, int pos){
    //打算放下一个皇后
    for(int i = 1;i <= 8;i++){
        //i就是第pos号皇后打算放的列数
        bool isOK = true;
        for(int j = 0; j<pos;j++){
            //j用来遍历之前已经放好的皇后0~pos-1
            if(queen[j] == i||//第j号皇后与pos号皇后在同一列
            pos - j == queen[j]-i||pos - j == i - queen[j]
            //在一条斜对角线上
            ){
                isOK = false;
                break;
            }
            if(isOK == true){
                //将pos号皇后放置好
                queen.push_back(i);
                if(pos == 7){
                    //八个皇后都已经放好了
                    queenvec.push_back(queen);
                }
                else {
                    DFSFindQueen(queen, pos+1);
                }
                queen.pop_back();//为下一种i的可能性做准备
            }
        }
    }
}
int main(){
    vector<int> queen;//用来记录已经放好的皇后的位置
    DFSFindQueen(queen, 0);
    
    
    return 0;
}

取巧的方法:如果解的数量有限,而且算解的时间很长。可以先写一个比较差的算法先生成所有的解,保存所有的解,写入代码,访问时直接访问解集合即可。著名的打表法。

#include <stdio.h>
#include <vector>
using namespace std;
//希望所有的函数都能访问queenvec
vector<vector<int>> queenvec;//所有的合法皇后序列
void DFSFindQueen(vector<int> &queen, int pos){
    //打算放下一个皇后
    for(int i = 1;i <= 8;i++){
        //i就是第pos号皇后打算放的列数
        bool isOK = true;
        for(int j = 0; j<pos;j++){
            //j用来遍历之前已经放好的皇后0~pos-1
            if(queen[j] == i||//第j号皇后与pos号皇后在同一列
            pos - j == queen[j]-i||pos - j == i - queen[j]
            //在一条斜对角线上
            ){
                isOK = false;
                break;
            }
        }
            if(isOK == true){
                //将pos号皇后放置好
                queen.push_back(i);
                if(pos == 7){
                    //八个皇后都已经放好了
                    queenvec.push_back(queen);
                    // printf("\"");
                    // for(int k = 0; k<8;k++){
                    //     printf("%d", queen[k]);
                    // }
                    // printf("\",\n");
                }
                else {
                    DFSFindQueen(queen, pos+1);
                }
                queen.pop_back();//为下一种i的可能性做准备
            }
    }
}
int main(){
    vector<int> queen;//用来记录已经放好的皇后的位置
    DFSFindQueen(queen, 0);
    int n;
    scanf("%d", &n);
    for(int i = 0; i<n;i++){
        int b;
        scanf("%d", &b);
        for(int j = 0; j < queenvec[b-1].size();j++){
            printf("%d", queenvec[b-1][j]);
        }
        printf("\n");
    }
    
    return 0;
}

第六题是数组划分。直接超时被拿下。

#include <vector>
#include <stdio.h>
using namespace std;
int sum = 0;//记录数组的和
int diff = 0;//记录遍历过程中最小的差值
void DFSFindMinDiff(vector<int> &arr, int pos, int sa){
    if(pos == arr.size()){
        return;
    }
    //arr[pos]不放入集合中
    DFSFindMinDiff(arr, pos+1,sa);
    //arr[pos]放入集合中
    int newdiff;//记录当前的差值
    if(2*(sa+arr[pos]) > sum){
        newdiff = 2*(sa+arr[pos])-sum;
    }
    else newdiff = sum - 2*(sa+arr[pos]);
    if(newdiff < diff){
        diff = newdiff;
    }
    DFSFindMinDiff(arr, pos+1, sa+arr[pos]);
}
int main(){
    vector<int> arr;
    int i;
    while(scanf("%d", &i)!=EOF){
        arr.push_back(i);
    }
    for(int i = 0; i<arr.size();i++){
        sum += arr[i];
    }
    diff = sum;
    DFSFindMinDiff(arr, 0, 0);
    //sa+sb = sum
    //sb-sa = diff
    int sa = (sum+diff)/2;
    int sb = (sum - diff)/2;
    printf("%d%d", sa, sb);
    
}

对代码进行剪枝,去掉不必要的搜索过程,尽快找到最优解,提前终止搜索。可以排序arr,先执行加入arr[pos],再执行不加入。一开始sa<sb,中间某个时刻sb大,此时没有必要继续。若diff为0或为1,终止,其次arr[pos]>sum/2。

#define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    using namespace std;
    int sum = 0; //记录数组的和
    int diff = 0; // 记录遍历过程最小的差值
    bool exitFlag = false; // 记录是否要提前退出
    void DFSFindMinDiff(vector<int> &arr, int pos, int sa) {
        if (pos == arr.size() || exitFlag == true) {
            return;
        }
        
        // arr[pos] 放入到 a集合当中
        int newdiff; //记录当前的差值
        if (2 * (sa + arr[pos]) - sum > 0) {
            newdiff = 2 * (sa + arr[pos]) - sum;
        }
        else {
            newdiff = sum - 2 * (sa + arr[pos]);
        }
    
        if (newdiff < diff) {
            diff = newdiff;
            if (diff == 0 || diff == 1 || 2 * arr[pos] > sum) {
                exitFlag = true;
            }
        }
    
        if (2 * (sa + arr[pos]) - sum < 0) {
            DFSFindMinDiff(arr, pos + 1, sa + arr[pos]);
        }
        
    
        // arr[pos] 不放入 a集合中
        DFSFindMinDiff(arr, pos + 1, sa);
    }
    bool compare(int lhs, int rhs) {
        return lhs > rhs;
    }
    int main() {
        vector<int> arr;
        int i;
        while (scanf("%d", &i) != EOF) {
            arr.push_back(i);
        }
        for (int i = 0; i < arr.size(); ++i) {
            sum += arr[i];
        }
        diff = sum;
        sort(arr.begin(), arr.end(), compare);
        DFSFindMinDiff(arr, 0, 0);
        // sa+sb = sum
        // sb-sa = diff
        int sa = (sum - diff) / 2;
        int sb = sa + diff;
        printf("%d %d\n", sb, sa);
        return 0;
    }

兄弟们轻易别跨考,要补的东西太多了,博主现在天天都在焦虑。祝刷到的兄弟们一切顺利吧,能考上出个帖子给大伙讲讲备考过程。

相关文章:

  • JVM 内存模型
  • python-leetcode-定长子串中元音的最大数目
  • 树莓百度百科更新!宜宾园区新业务板块全解析
  • 【Java--数据结构】优先级队列( PriorityQueue)
  • 【从零开始学习计算机科学】编程语言(二)名字、关键字、保留字 与 变量
  • CentOS7上面搭建sentry24版本详细教程与踩坑记录
  • Elixir语言的容量规划
  • 概率论的基本知识
  • NPU的应用场景:从云端到边缘
  • 如何将实际入账的统计内部订单主数据修改成本中心
  • ADB报错:daemon not running...
  • 新闻网页信息抽取
  • 【Node.js入门笔记5---fs文件信息与元数据】
  • Masked Autoencoders Are Scalable Vision Learners——论文学习
  • 2025年人工智能应用全景解析:从医疗革命到产业重构的深度实践
  • coding ability 展开第三幕(滑动指针——基础篇)超详细!!!!
  • 【农业大数据处理与应用】实验四 多元回归与随机森林模型在植被病虫害监测中的对比分析
  • 浅谈时钟启动和Systemlnit函数
  • 管理学习状态
  • 精准车型识别:视觉分析技术的力量
  • 武汉建设信息网站/宁波seo排名公司
  • 问佛教网站大师做早课烧香烛可以吗/公司产品推广文案
  • 中国企业500强厉害吗/公司百度官网优化
  • 阜蒙县建设镇网站/无需下载直接进入的网站的代码
  • 高端网站设计公司新鸿儒/最彻底的手机优化软件
  • 如何判断网站有cdn加速/搜索引擎营销案例有哪些