kuangbin专题—简单搜索

kuangbin专题—简单搜索

  • 1. 棋盘问题
  • 2. 地牢大师
  • 3. 抓住那头牛
  • 4.翻转

在acwing上发现这个活动,写来玩玩,持续更新中~
kuangbin专题打卡活动

1. 棋盘问题

题意概括:
在一个 n × n n\times n n×n 的棋盘上放 k k k 个棋子的方案数,每行每列只能放一个,且只能放在值为 ‘#’ 的格子上。
n ≤ 8 , k ≤ n n\le 8,k\le n n8,kn

思路:
和八皇后问题的思路类似,递归每一行,在其中选择出一列放置,或者是该行不放,当放够了 k k k 个棋子结果方案数加一并返回,递归到的行数超出了棋盘的大小则直接返回。

code:

#include 
using namespace std;

char g[10][10];
bool vis[10];  // 第 j 列是否访问过
int n, k, res;

void dfs(int i, int cnt) {
    if (k == cnt) { res ++; return; } // 放满了k个
    if (i >= n)     return;  // 超出棋盘行数
    for (int j = 0; j < n; j++) { // 第 i 行放在第 j 列
        if (vis[j] || g[i][j] == '.') continue;
        vis[j] = true;
        dfs(i + 1, cnt + 1);
        vis[j] = false;
    }
    dfs(i + 1, cnt); // 第 i 行不放
}

int main() {
    while (scanf("%d%d", &n, &k)) {
        if (n == -1) break;
        for (int i = 0; i < n; i++) scanf("%s", &g[i]);
        res = 0;
        memset(vis, false, sizeof(vis));
        dfs(0, 0);
        printf("%d\n", res);
    }
    return 0;
}

无关紧要的状态优化:
如果擅长位运算,可以把代码写得简短一点,用一个 int 变量来代替 vis 数组记录每一列是否访问过。

code:

#include 
using namespace std;

char g[10][10];
int n, k, res;

// state:用该数二进制位表示第j列是否被占用
void dfs(int i, int cnt, int state) {
    if (k == cnt) { res ++; return; } // 放满了k个
    if (i >= n)     return;  // 超出棋盘行数
    for (int j = 0; j < n; j++) { // 第 i 行放在第 j 列
        if ((state >> j & 1) || g[i][j] == '.') continue;
        dfs(i + 1, cnt + 1, state | (1 << j));
    }
    dfs(i + 1, cnt); // 第 i 行不放
}

int main() {
    while (scanf("%d%d", &n, &k)) {
        if (n == -1) break;
        for (int i = 0; i < n; i++) scanf("%s", &g[i]);
        res = 0;
        dfs(0, 0, 0);
        printf("%d\n", res);
    }
    return 0;
}

2. 地牢大师

题意概括:
你在一个立方体中,每次可以向东南西北上下六个方向移动,从起点 S S S 开始到终点 E E E# 代表代表该位置有障碍走不通,求起点到终点的最小步数。

思路:
标准的BFS模板,不过是从从二维拓展到了三维,按照二维的思路写就行了。

#include 
using namespace std;

// 地牢大师

int L, R, C;
char g[104][104][104];  // 存图
bool vis[104][104][104];  // 记录是否访问过
int step[104][104][104]; // 存 (i,j,k) 到起点的距离
int dd[6][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}}; 
int sx, sy, sz; // 起始坐标
struct pos { // 坐标结构体
    int x, y, z;
};

int bfs() {
    queue<pos> q;
    q.push({sx, sy, sz});
    vis[sx][sy][sz] = true;
    while (q.size()) {
        pos p = q.front(); q.pop();
        if (g[p.x][p.y][p.z] == 'E') return step[p.x][p.y][p.z];
        for (auto di: dd) {
            int nx = p.x + di[0], ny = p.y + di[1], nz = p.z + di[2];
            if (0>nx || 0>ny || 0>nz || nx>=L || ny>=R || nz>=C || vis[nx][ny][nz] || g[nx][ny][nz]=='#') continue;
            step[nx][ny][nz] = 1 + step[p.x][p.y][p.z];
            q.push({nx, ny, nz});
            vis[nx][ny][nz] = true;
        }
    }
    return -1;
}

int main() {
    while (cin >> L >> R >> C, L && R && C) {
        memset(vis, false, sizeof(vis));
        memset(step, 0, sizeof(step));
        for (int i = 0; i < L; i++) {
            for (int j = 0; j < R; j++) {
                for (int k = 0; k < C; k++) {
                    cin >> g[i][j][k];
                    if (g[i][j][k] == 'S') sx = i, sy = j, sz = k;
                }
            }
        }
        int res = bfs();
        if (~res) cout << "Escaped in " << res << " minute(s)." << endl;
        else cout << "Trapped!" << endl;
    }
    

    return 0;
}

3. 抓住那头牛

手机号没绑定做不了那道题,等后面解决了再写

4.翻转

题意概括:
给你一个 m × n m \times n m×n 且只包含 0 0 0 1 1 1 的矩阵,每次点击一个位置,当前位置和上下左右四个位置进行 01 01 01互换,要求找出最少翻转次数将矩阵转换为全 0 0 0,并记录每个位置点击了多少次,输出字典序最小点击次数。

思路:
二进制枚举第一行所有翻转的方案,然后翻转第一行,后面的行根据当前位置的上方格子是否为 1 1 1 来判断是否翻转当前格子,如果上方为 1 1 1 则需要翻转当前格子,当最后一行翻转完之后判断最后一行是否全 0 0 0,是则直接输出结果返回,如果第一行的所有方案枚举完最后一行都不能全 0 0 0,则不能将矩阵转换为全 0 0 0

因为每次翻转完会改变原矩阵,所以需要一个备份矩阵来记录输入。

相似题目:acwing算法竞赛进阶指南打卡活动–费解的开关

code:

#include 
using namespace std;

int m, n;
int g[16][16], bak[16][16], res[16][16]; // 翻转矩阵,备份矩阵,点击次数矩阵
int dd[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};

void turn(int i, int j) { // 翻转
    g[i][j] ^= 1;
    for (auto di: dd) {
        int nx = i + di[0], ny = j + di[1];
        if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
        g[nx][ny] ^= 1;
    }
}

void solve() {
    for (int i = 0; i < 1 << n; i++) {
        memcpy(g, bak, sizeof(g));
        memset(res, 0, sizeof(res));
        for (int j = 0; j < n; j++)  // 先按第一行
            if (i >> j & 1) turn(0, j), res[0][j] ++;
        for (int x = 1; x < m; x++)  // 按后面的行
            for (int y = 0; y < n; y++) 
                if (g[x-1][y] == 1) turn(x, y), res[x][y] ++;
        bool flag = true;
        for (int j = 0; j < n; j++)  // 判断最后一行是否全零
            if (g[m-1][j] == 1) { flag = false; break; }
        if (flag) {
            for (int x = 0; x < m; x++) {
                for (int y = 0; y < n; y++) {
                    printf("%d ", res[x][y]);
                }
                puts("");
            }
            return;
        }
    }
    puts("IMPOSSIBLE");
}

int main() {
    scanf("%d%d", &m, &n);
    for (int i = 0; i < m; i++)
        for (int j = 0; j < n; j++)
            scanf("%d", &bak[i][j]);
    solve();
    return 0;
}

你可能感兴趣的:(kuangbin题单,算法,dfs,bfs)