信息学奥赛一本通:深搜与回溯

文章讨论了在给定的二维空间环境中,如房子的瓷砖布局、国际象棋皇后问题、马的移动规则以及迷宫路径探索,利用编程解决如何计算可达区域、放置皇后避免攻击及寻找路径的问题,涉及回溯算法和图形搜索策略。
摘要由CSDN通过智能技术生成

1216:红与黑


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 40665     通过数: 16737

【题目描述】

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

【输入】

包括多组数据。每组数据的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下:

1)‘.’:黑色的瓷砖;

2)‘#’:红色的瓷砖;

3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每组数据中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

【输出】

对每组数据,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

【输入样例】

6 9 
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0

【输出样例】

45

【代码示例】

#include<iostream>
using namespace std;
char a[21][21];
int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int n, m, x1, y1, count;
void dfs(int x, int y){
    for(int i = 0; i < 4; i++){
        int xx = x + d[i][0];
        int yy = y + d[i][1];
        if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && a[xx][yy] == '.'){
            count++;
            a[xx][yy] = '#';
            dfs(xx, yy);
        }
    }
}
int main(){
    cin >> m >> n;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> a[i][j];
            if(a[i][j] == '@'){
                x1 = i;
                y1 = j;
            }
        }
    }
    count++;
    dfs(x1, y1);
    cout << count << endl;
    return 0;
}

1213:8皇后问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 44790     通过数: 16952

【题目描述】

在国际象棋棋盘上放置8

个皇后,要求每两个皇后之间不能直接吃掉对方。

【输入】

(无)

【输出】

按给定顺序和格式输出所有八皇后问题的解(见样例)。

【输入样例】

(无)

【输出样例】

No. 1
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 1 0 0 0 0 0 
No. 2
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 0 1 0 0 0 0 0 
...以下省略

【代码】

#include <iostream>
using namespace std;
int a[9], v1[101], v2[101], v3[101], cnt;//a[i]表示第 i 行的皇后在第几列上
// v1[i]表示第 i 列有没有皇后(有为 1,无为 0)
// v2[x - y + 8]表示坐标为 (x, y) 的点所在的右斜线(\)有没有皇后(有为 1,无为 0)
// v3[x + y]表示坐标为 (x, y) 的点所在的左斜线(/)有没有皇后(有为 1,无为 0)
void dfs(int x) {
    if(x > 8) {//八个皇后放完了,输出
    cout << "No." << ++cnt << endl;
        printf("No. %d\n", ++cnt);
        for(int i = 1; i < 9; ++i) {
            for(int j = 1; j < 9; ++j) {
                if(a[j] == i){
                    cout << "1 " << endl;
                }else{
                    cout << "0 " << endl;
                }    
            }
            puts("");
        }
    } else {
        for(int i = 1; i < 9; ++i) {
            if(!v1[i] && !v2[x - i + 8] && !v3[x + i]) {
            // 坐标为 (x, i) 的点没有被其他皇后占领,此点可以放皇后
                a[x] = i;// 第 x 行的皇后放在了第 i 列
                v1[i] = 1;// 占领列
                v2[x - i + 8] = 1;// 占领右斜线
                v3[x + i] = 1;// 占领左斜线
                dfs(x + 1);// 在下一行放皇后
                v1[i] = 0;// 回溯
                v2[x - i + 8] = 0;
                v3[x + i] = 0;
            }
        }
    }
}
int main() {
    dfs(1);// 从第一行开始放
    return 0;
}

1219:马走日


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 29872     通过数: 15673

【题目描述】

马在中国象棋以日字形规则移动。

请编写一段程序,给定n×m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

【输入】

第一行为整数T(T < 10),表示测试数据组数。

每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标n,m,x,y。(0≤x≤n-1,0≤y≤m-1, m < 10, n < 10)。

【输出】

每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,0为无法遍历一次。

【输入样例】

1
5 4 0 0

【输出样例】

32

【代码示例】

#include<bits/stdc++.h>
using namespace std;
int a[100][100];
int fx[9] = {0, -1, 1, 2, 2, 1, -1, -2, -2};
int fy[9] = {0, 2, 2, 1, -1, -2, -2, -1, 1};
int n, m, x, y, cnt = 0;
void dfs(int x, int y, int sum){
    if(sum == n * m){ //表明所有格子已走完
        cnt++;
        return;
    }else{
        for(int i = 1; i <= 8; i++){//遍历8个方向
            int xx = x + fx[i], yy = y + fy[i];
            if(xx >= 0 && xx < n && yy >= 0 && yy < m && a[xx][yy] == 0){
             //待探索的格子是否合法
                a[x][y] = 1;    //当前的格子先标记为已遍历
                dfs(xx, yy, sum + 1);
                a[x][y] = 0;  //遍历结束,回退的格子状态还原,回溯
            }
        }  
    }
}
int main(){
    int t;
    cin >> t;
    while(t--){//读入t组测试数据
        cin >> n >> m >> x >> y;//读入数据
        dfs(x, y, 1);    
        cout << cnt << endl;
        cnt = 0;   //本轮计算结束,结果归零等待下一次统计
    }
    return 0;
}

 

1215:迷宫


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 67224     通过数: 21717

【题目描述】

一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n×n

的格点组成,每个格点只有2

种状态,.#,前者表示可以通行后者表示不能通行。同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。如果起点或者终点有一个不能通行(为#),则看成无法办到。

【输入】

第1行是测试数据的组数k

,后面跟着k组输入。每组测试数据的第1行是一个正整数n(1≤n≤100),表示迷宫的规模是n×n的。接下来是一个n×n的矩阵,矩阵中的元素为.或者#。再接下来一行是4个整数ha,la,hb,lb,描述A处在第ha行, 第la列,B处在第hb行, 第lb列。注意到ha,la,hb,lb全部是从0

开始计数的。

【输出】

k

行,每行输出对应一个输入。能办到则输出“YES”,否则输出“NO”。

【输入样例】

2
3
.##
..#
#..
0 0 2 2
5
.....
###.#
..#..
###..
...#.
0 0 4 0

【输出样例】

YES
NO

【代码示例】

#include <bits/stdc++.h>
using namespace std;
#define N 105
int n, ha, la, hb, lb;
char mp[1001][1001];
bool vis[1001][1001];
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
bool flag;
void dfs(int x, int y){
    if(flag){//剪枝:如果发现已经能到了,就不再递归运行了
        return;
    }
    for(int i = 0; i < 4; i++){
        int xx = x + dir[i][0], yy = y + dir[i][1];
        if(xx >= 0 && xx < n && yy >= 0 && yy < n && vis[xx][yy] == false &&
         mp[xx][yy] == '.'){
            vis[x][y] = true;
            if(xx == hb && yy == lb){
                flag = true;
                return;    
            }else{
                dfs(xx, yy);
            }  
        }
    }
}
int main(){//判断起点终点是否在一个连通块内
    int k;
    cin >> k;
    while(k--){
        cin >> n;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                cin >> mp[i][j];
            }
        }      
        cin >> ha >> la >> hb >> lb;
        memset(vis, 0, sizeof(vis));
        flag = false;
        vis[ha][la] = true;
        if(ha == hb && la == lb){//如果起点和终点重合,直接就能到达
            flag = true;
        }
        if(mp[ha][la] != '#' && mp[hb][lb] != '#'){
            dfs(ha, la);
        }  
        if(flag){
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值