算法基础模板整理(基础搜索篇)

递归实现枚举

递归实现指数型枚举

void dfs(int k){
    if(k > n) {
        for(auto &x : res) cout << x << ' ';
        cout << endl;  return;
    }
    
    dfs(k + 1);
    
    res.push_back(k);
    dfs(k + 1);
    res.pop_back();
}

递归实现排列型枚举

void dfs(int k){
    if(k > n){
        for(int i = 1; i <= n; i ++ ) cout << a[i] << ' ';
        cout << endl;  return;
    }
    
    for(int i = 1; i <= n; i ++ ){
        if(st[i]) continue;
        st[i] = true;
        a[k] = i;
        dfs(k + 1);
        st[i] = false;
    }
}

递归实现组合型枚举

void dfs(int k){
    int nn = res.size();
    if(nn > m || m - nn > n - k + 1) return;
    if(k > n){
        for(auto &x : res) cout << x << ' ';
        cout << endl;
    }
    
    res.push_back(k);
    dfs(k + 1);
    res.pop_back();
    
    dfs(k + 1);
}              


BFS求最短路

void bfs(){           // 经典二维数组中 bfs 求最短路
    queue<pii> q;
    q.push({0, 0});
    vis[0][0] = true;
    
    int res = 0;
    while(!q.empty()){
        int nn = q.size();
        for(int i = 0; i < nn; i ++ ){
            auto [x, y] = q.front();
            q.pop();
            if(x == n - 1 && y == m - 1) {
                cout << res;
                return;
            }
            for(int k = 0; k < 4; k ++ ){
                int nx = x + dx[k], ny = y + dy[k];
                if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                if(vis[nx][ny] || g[nx][ny]) continue;
                q.push({nx, ny});
                vis[nx][ny] = true;
            }
        }
        res ++ ;  //也可开一个dist数组来记录最短路 且可以恰好省去vis数组
    }
}

int bfs(){               // 树 or 图 上求点的层次
    queue<int> q; q.push(1);
    memset(d, -1, sizeof(d));
    d[1] = 0;
    
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i = h[u]; i != -1; i = ne[i]){
            int v = e[i];
            if(d[v] != -1) continue;
            q.push(v), d[v] = d[u] + 1;
        }
    }
    return d[n];
}


DFS判断连通性

bool dfs(int x, int y){      //判断两点是否可达
    vis[x][y] = true;
    if(x == desx && y == desy) return true;
    for(int k = 0; k < 4; k ++ ){
        int nx = x + dx[k], ny = y + dy[k];
        if(nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
        if(vis[nx][ny] || g[nx][ny] == '#') continue;
        if(dfs(nx, ny)) return true;
    }
    return false;
}


//----------------------------------------

void dfs(int x, int y){    //求连通块个数    也可用bfs
    vis[x][y] = true;
    for(int k = 0; k < 8; k ++ ){
        int nx = x + dx[k], ny = y + dy[k];
        if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
        if(vis[nx][ny] || g[nx][ny] == '#') continue;
        dfs(nx, ny);
    }
}

for(int i = 0; i < n; i ++ )
    for(int j = 0; j < m; j ++ )
        if(g[i][j] == '.' && !vis[i][j])
            dfs(i, j), ans ++ ;


DFS求树的直径

int c;   //记录第一次dfs得到的端点
void dfs(int u, int fa){
    for(auto &[v, w] : e[u])
        if(v != fa){
            dist[v] = dist[u] + w;
            if(dist[v] > dist[c]) c = v;  //记录最远的点
            dfs(v, u);
        }
}

int main(){
    cin >> n;
    for(int i = 1; i < n; i ++ ){
        int u, v, w; cin >> u >> v >> w;
        e[u].push_back(make_pair(v, w));
        e[v].push_back(make_pair(u, w));
    }
    
    dfs(1, 0);     //找到一个端点c
    dist[c] = 0;
    dfs(c, 0);     //找到c对应的端点
    
    cout << dist[c];
}

DFS求树的重心

int dfs(int u){
    vis[u] = true;
    int sum = 1, res = 0;  
    for(int i = h[u]; i != -1; i = ne[i]){
        int v = e[i];
        if(!vis[v]){
            int s = dfs(v);
            res = max(res, s);
            sum += s;
        }
    }
    res = max(res, n - sum);//以u为起点向下的最大连通块 向上的连通块 较大者
    ans = min(res, ans);
    return sum;
}


回溯

DFS搜索经典案例(Acwing 1116.马走日)

void dfs(int x, int y, int cnt){
    if(cnt == n * m){   //走满图中所有的点
        res ++ ;
        return;
    }
    
    for(int k = 0; k < 8; k ++ ){
        int nx = x + dx[k], ny = y + dy[k];
        if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
        if(vis[nx][ny]) continue;
        
        vis[nx][ny] = true;
        dfs(nx, ny, cnt + 1);
        vis[nx][ny] = false;
    }
}

DFS剪枝经典案例(Acwing 165.小猫爬山)

void dfs(int k, int cnt){
    if(cnt >= res) return;   //剪枝
    if(k > n){
        res = min(res, cnt);
        return;
    }
    //枚举已有的车是否可存
    for(int i = 1; i <= cnt; i ++ ){
        if(t[i] + a[k] <= W){
            t[i] += a[k];
            dfs(k + 1, cnt);  
            t[i] -= a[k];
        }
    }

    t[cnt + 1] += a[k];
    dfs(k + 1, cnt + 1);   //新开一辆车
    t[cnt + 1] -= a[k];
}



状态压缩 递推

经典示例(Acwing 95.费解的开关)

void turn(int x, int y){
    for(int k = 0; k < 5; k ++ ){
        int nx = x + dx[k], ny = y + dy[k];
        if(nx < 0 || nx >= 5 || ny < 0 || ny >= 5) continue;
        g[nx][ny] ^= 1;
    }
}

int main(){
    int t; cin >> t;
    while(t -- ){
        for(int i = 0; i < 5; i ++ ) cin >> g[i];
        int ans = 1e7;
        for(int k = 0; k < 1 << 5; k ++ ){
            int step = 0;
            memcpy(backup, g, sizeof(g));
            //枚举第一行如何操作
            for(int i = 0; i < 5; i ++ )
                if(k >> i & 1){
                    step ++ ;
                    turn(0, i);
                }
            //从第一行开始到第四行,依次改变下一行的对应位置的开关
            for(int i = 0; i < 4; i ++ )
                for(int j = 0; j < 5; j ++ )
                    if(g[i][j] == '0'){
                        step ++ ;
                        turn(i + 1, j);
                    }
            //验证最后一行是否都是亮的
            bool dark = false;
            for(int i = 0; i < 5; i ++ )
                if(g[4][i] == '0'){
                    dark = true;
                    break;
                }
            if(!dark) ans = min(ans, step);
            memcpy(g, backup, sizeof(backup));
        }
        if(ans <= 6) cout << ans << endl;
        else cout << -1 << endl;
    }
}
posted @ 2023-04-14 01:05  MarisaMagic  阅读(21)  评论(0编辑  收藏  举报