题目汇总

120 阅读1分钟

搜索

技巧 :

  1. 搜索题中总是会出现 二维坐标 问题, 棋子 总是被不断交换进行行走, 这时我们就可以用方向矢量 来进行原坐标 x, y结合, 而存坐标用 pair 存简直天造地设, 但要方便使用还得 typedef pair<int, int> PII;

  2. 用 BFS 求某些最短路问题的时候( 比如各种变换棋盘 ), 关键是找到距离数组,但是这个数组不一定是 " 真数组 ", 也可以用于存各种状态的 unordered_map<T, T>

DFS

模板

设置容器
设置元素标识符

void dfs(int u){
    
    if 判断是否满足到头条件
    
    for 循环遍历各变量
        if 进行剪枝
            标识符设为 true
            更新
            dfs(u + 1)
            标识符设为 false
            恢复现场
}

排列数字

image.png

image.png

N皇后

image.png

#include <iostream>

using namespace std;

const int N = 10;

char g[N][N];   
bool col[N], dg[2 * N], udg[2 * N];
int n;

void dfs(int u){
    //三刷时受上题排列数字影响, u > n,其实上题从 1 开始
    if(u == n){
        for(int i = 0; i < n; i++){
            // cout << g[i] << endl;
            puts(g[i]);
        }
        puts("");
        return;
    }
    
    for(int i = 0; i < n; i++){
        if(!col[i] && !dg[u + i] && !udg[u - i + n]){
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[u - i + n] = true;
            dfs(u + 1);
            g[u][i] = '.';
            col[i] = dg[u + i] = udg[u - i + n] = false;
        }
    }
}

int main(){
    cin >> n;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            g[i][j] = '.';
        }
    }
    
    dfs(0);
    
    return 0;
}

疑问

1. 啥时候要恢复现场, 为什么有的时候没有进行恢复的操作(比如 拓扑排序 那道题)

恢复现场是 DFS 专属的操作, BFS 绝对不可能有, 而恢复现场是为下个操作进行的, 但如果给定了一个关系(比如一棵给定的树, 那就不需要恢复现场了)

总结

  1. DFS 本质是递归

BFS

模板

设置储存**队列**
设置元素标识符

int bfs(){
    寻找队头放进队列
    while 队列不为空
        扩展队列
        if !st[遍历x]
            queue <- x
            d[x] = d[t] + 1
}

走迷宫

image.png

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

using namespace std;

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

int g[N][N];
int d[N][N];
PII q[N * N];
int n, m;

int bfs(){
    int hh = 0, tt = 0;    //注意我想提前放进去了一个数, 故 tt 为 0 而不是 -1
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    d[0][0] = 0;
    q[0] = {0, 0};   //这样的话 hh <= tt 岂不是失效了? 不是,因为我没有通过压数的方式放进队列
    while(hh <= tt){
        auto t = q[hh++];
        for(int i = 0; i < 4; i++){
            int x = t.first + dx[i];
            int y = t.second + dy[i];
            if(d[x][y] == -1 && g[x][y] == 0 && x >= 0 && x < n && y >= 0 && y < m){
                d[x][y] = d[t.first][t.second] + 1;
                q[++tt] = {x, y};
            }
        }
    }
    
    return d[n - 1][m - 1];
}

int main(){
    memset(d, -1, sizeof d);
    cin >> n >> m;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            cin >> g[i][j];
        }
    }
    
    cout << bfs() << endl;
    
    return 0;
    
}

八数码

image.png

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

const int N = 3;
string start;
queue<string> q;
unordered_map<string, int> d;

int bfs(){
    string end = "12345678x";
    q.push(start);
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    
    while(q.size()){
        auto state = q.front();
        auto temp = state;
        q.pop();
        if(state == end)    return d[end];
        int k = state.find('x');
        
        for(int i = 0; i < 4; i++){
            int x = k / 3 + dx[i];
            int y = k % 3 + dy[i];
            if(x >= 0 && x < 3 && y >= 0 && y < 3)      //这里难道不需要判定没走过吗,不然一直上下咋说
            {
                swap(state[3 * x + y], state[k]);
                if(!d.count(state))     //额 这里进行剪枝判定,都差不多
                {
                    q.push(state);
                    d[state] = d[temp] + 1;
                    // if(state == end)    return d[end];   放这里会初始就是完成体,把完成体换了就会-1
                }
                
                swap(state[3 * x + y], state[k]);   //还原 一是没有回溯, 二是直接操作给定棋盘, 上面走迷宫是更新d, 迷宫没改
                
            }
        }
    }
    
    return -1;
}

int main(){
    string str, state;
    for(int i = 0; i < 9; i++){
        cin >> str;
        start += str;
    }
    
    cout << bfs() << endl;
    
    return 0;
}

疑问

总结