3.8深度优先搜索dfs

3.8深度优先搜索dfs

洛谷题目传送门

P1451 求细胞数量

【题目描述】
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,
细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,
求给定矩形阵列的细胞个数。

【输入格式】
第 1 行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个 n×m 的矩阵。

【输出格式】
一行一个整数代表细胞个数。

输入样例:

4 10
0234500067
1034560500
2045600671
0000000089

输出样例: 4
数据规模与约定:对于 100% 的数据,保证 1≤n,m≤100。

【参考题解】

#include<bits/stdc++.h>
using namespace std;

const int N=110;
char a[N][N];
int dx[8]={-1, 0, 1, 0};
int dy[8]={ 0,-1, 0, 1};

struct T{
    int x,y;
};

int n,m,ans=0;
bool in(int x, int y){
    if(x>=0&&x<n&&y>=0&&y<m) return 1;
    return 0;
}
void print(){
    for(int i=0; i<n; i++) printf("%s\n",a[i]);
    printf("\n");
}

void dfs(int x,int y){
    a[x][y]='0';
    for(int i=0; i<4; i++){
        int tx=x+dx[i];
        int ty=y+dy[i];
        if(in(tx,ty) && a[tx][ty]!='0'){
            dfs(tx,ty);
        }
    }
}
int main(){
//    freopen("data.in", "r", stdin);
//    freopen("data.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i=0; i<n; i++) scanf("%s", a[i]);
//    print();
    for(int i=0; i<n; i++){
        for(int j=0; j<m; j++){
            if(a[i][j]!='0'){
                dfs(i,j); ans++;
//                print();
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

P1596 [USACO10OCT]Lake Counting S

【题目描述】
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。
我们用一个 NxM(1<=N<=100;1<=M<=100) 网格图表示。
每个网格中有水('W') 或是旱地('.')。
一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。
约翰想弄清楚他的田地已经形成了多少水坑。
给出约翰田地的示意图,确定当中有多少水坑。

【输入格式】
第 1 行, 两个空格隔开的整数:N 和 M ,
第 2 行到第 N+1 行, 每行 M 个字符,每个字符是 'W' 或 '.',它们表示网格图中的一排。
字符之间没有空格。

【输出格式】
一行,水坑的数量

输入样例:

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

输出样例: 3

【参考题解】

#include<bits/stdc++.h>
using namespace std;

const int N=110;
char a[N][N];
int n,m,ans=0;

int dx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={-1, 0, 1,-1, 1,-1, 0, 1};

bool in(int x, int y){
    if(x>=0&&x<n&&y>=0&&y<m) return 1;
    return 0;
}

void dfs(int x, int y){
    a[x][y]='.';
    for(int i=0; i<8; i++){
        int tx=x+dx[i];
        int ty=y+dy[i];
        if(in(tx,ty) && a[tx][ty]=='W'){
            dfs(tx,ty);
        }
    }
}

void print(){
    for(int i=0; i<n; i++){
        printf("%s\n",a[i]);
    }printf("\n");
}

int main(){
//    freopen("data.in", "r", stdin);
//    freopen("data.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i=0; i<n; i++){
        scanf("%s",a[i]);
    }
    for(int i=0; i<n; i++){
        for(int j=0; j<m; j++){
            if(a[i][j]=='W') {
                dfs(i,j);  ans++;
//                print(); //中间打印查看问题,要养成好习惯
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

UVA572 油田 Oil Deposits

【题目描述】
输入多个 m 行 n 列的矩阵,用 0 0 表示输入结束。
找出有多少块石油区域,用 “@” 代表石油,假如两个 “@” 在横,竖或对角线上相邻,
就说它们位于同一区域,对于每个输入,输出一个数表示有几个石油区域。

输入样例:

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0

输出样例:

0
1
2
2

【参考题解】

#include<bits/stdc++.h>
using namespace std;

const int N=110;
int n,m;
string s[N];
int dx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={-1, 0, 1,-1, 1,-1, 0, 1};

bool in(int x, int y){
    if(x>=0&&x<n &&y>=0&&y<m) return 1;
    return 0;
}
void dfs(int x, int y){
    s[x][y]='*';
    for(int i=0; i<8; i++){
        int tx=x+dx[i];
        int ty=y+dy[i];
        if(in(tx,ty) && s[tx][ty]=='@'){
            dfs(tx,ty);
        }
    }
}
int main(){
//    freopen("data.in", "r", stdin);
    while(scanf("%d%d", &n, &m)==2){
        if(n==0&&m==0) return 0;
        for(int i=0; i<n; i++) cin>>s[i];
        int ans=0; //多组数据,需要每次都初始化为 0
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(s[i][j]=='@'){
                    dfs(i,j); ans++;
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

P5461 赦免战俘

【题目描述】kkksc03 决定赦免一些作弊者。
现有 2^n × 2^n (n≤10) 名作弊者站成一个正方形方阵等候 kkksc03 的发落。
他将正方形矩阵均分为 4 个更小的正方形矩阵,每个更小的矩阵的边长是原矩阵的一半。
其中左上角那一个矩阵的所有作弊者都将得到赦免,剩下 3 个小矩阵中,
每一个矩阵继续分为 4 个更小的矩阵,然后通过同样的方式赦免作弊者...
直到矩阵无法再分下去为止。
所有没有被赦免的作弊者都将被处以棕名处罚。

给出 n,请输出每名作弊者的命运,其中 0 代表被赦免,1 代表不被赦免。

【输入格式】
一个整数 n。

【输出格式】
2^n × 2^n 的 01 矩阵,代表每个人是否被赦免。数字之间有一个空格。

输入样例: 3
输出样例:

0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1

【参考题解】

#include<cstdio>
#define N 1030
int a[N][N];

void print(int x1, int y1, int x2, int y2){
//    printf("(%2d, %2d) -> (%2d, %2d)\n",x1,y1,x2,y2);
     for(int i=x1; i<=x2; i++){
        for(int j=y1; j<y2; j++){
            printf("%d ", a[i][j] ? 0:1);
        }
        printf("%d\n", a[i][y2] ? 0:1);
    }
}
void dfs(int x1, int y1, int x2, int y2){
    if(x1==x2) return;
    for(int i=x1; i<=(x1+x2)/2; i++){
        for(int j=y1; j<=(y1+y2)/2; j++){
            a[i][j]=1;
        }
    }
//    print(x1, y1, x2, y2);
    dfs(x1, (y1+y2)/2+1, (x1+x2)/2, y2);
    dfs((x1+x2)/2+1, y1, x2, (y1+y2)/2);
    dfs((x1+x2)/2+1, (y1+y2)/2+1, x2, y2);
}

int main(){
    int n; scanf("%d", &n);
    n=(1<<n);
    dfs(1,1,n,n);
    print(1,1,n,n);
    return 0;
}

P1706 全排列问题

【题目描述】
按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,
要求所产生的任一数字序列中不允许出现重复的数字。

【输入格式】
一个整数 n。

【输出格式】
由 1~n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 5 个场宽。

输入样例: 3
输出样例:

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

说明/提示:1≤n≤9。

【参考题解】

#include<bits/stdc++.h>
using namespace std;
int n, a[10],vis[10];
void print(){
    for(int i=1; i<=n; i++){
        printf("%5d",a[i]);//cout<<setw(5)<<a[i];
    }printf("\n");
}
//安置第 m 个的元素
void dfs(int m){
    if(m>n){// Why not? m>=n, Because: true == (a[m]==0);
        print(); return ;//返回上一级,可以节省少许时间
    }
    for(int i=1; i<=n; i++){
        if(!vis[i]){
            a[m] = i;  vis[i] = 1;
            dfs(m+1);    // 继续安置第 m+1 列的元素
            vis[i] = 0;  //回溯,当前元素取消使用标记
        }
    }
}
int main(){
    scanf("%d", &n);
    dfs(1);
    return 0;
}

P1157 组合的输出

【题目描述】
排列与组合是常用的数学方法,其中组合就是从 n 个元素中抽出 r 个元素(不分顺序且 r≤n),
我们可以简单地将 n 个元素理解为自然数 1,2,…,n,从中任取 r 个数。
现要求你输出所有组合。

【输入格式】
一行两个自然数 n,r (1<n<21, 0≤r≤n)。

【输出格式】
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,
每个元素占三个字符的位置,所有的组合也按字典顺序。

输入样例: 5 3
输出样例:

  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5

【参考题解】

#include<iostream>
#include<iomanip>
using namespace std;
const int N=21;
int n, r, a[N], vis[N];
void dfs(int m){
    if(m>r){
        for(int i=1; i<=r; i++){
            cout<<setw(3)<<a[i];
        }cout<<endl;
        return;
    }
    for(int i=1; i<=n; i++){
        if(!vis[i] && i>a[m-1]){
            a[m] = i; vis[i] = 1;
            dfs(m+1);
            vis[i] = 0; //回溯
        }
    }
    return;
}

int main(){
    cin>>n>>r;
    dfs(1);
    return 0;
}

P1605 迷宫

【题目描述】
给定一个 N*M 方格的迷宫,迷宫里有 T 处障碍,障碍处不可通过。
给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。
在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

【输入格式】
第一行N、M和T,N为行,M为列,T为障碍总数。
第二行起点坐标 SX,SY,终点坐标 FX,FY。接下来 T 行,每行为障碍点的坐标。

【输出格式】
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入样例:

2 2 1
1 1 2 2
1 2

输出样例: 1
说明/提示: 1≤N,M≤5

【参考题解】

#include<iostream>
using namespace std;
const int N=10;
int a[N][N],vis[N][N];
int n, m, t, x1, x2, y1, y2, ans=0;

int dx[4]={-1, 0, 1, 0};
int dy[4]={ 0, 1, 0,-1};

void dfs(int x, int y){
    if(x<1 || y<1 || x>n || y>m ||a[x][y]==1) return;
    if(x==x2 && y==y2){
        ans++; return;
    }
    a[x][y]=1;//一个点只能走一次
    for(int i=0; i<4; i++){
       dfs(x+dx[i], y+dy[i]);
    }
    a[x][y]=0;
}

int main(){
    cin>>n>>m>>t;
    cin>>x1>>y1>>x2>>y2;
    for(int i=1; i<=t; i++) {
        int x,y; cin>>x>>y;
        a[x][y]=1;
    }
    dfs(x1,y1);
    cout<<ans;
    return 0;
}

EZOI 瓷砖

http://gdgzoi.com/

【题目描述】
在一个 w×h 的矩形广场上,每一块 1×1 的地面都铺设了红色或黑色的瓷砖。
小林同学站在某一块黑色的瓷砖上,他可以从此处出发,
移动到上、下、左、右四个相邻的且是黑色的瓷砖上。
现在,他想知道,通过重复上述移动所能经过的黑色瓷砖数。

【输入格式】
第 1 行为 h、w,2≤w、h≤50,之间由一个空格隔开;
以下为一个 w 行 h 列的二维字符矩阵,每个字符为“.”“#”“@”,
分别表示该位置为黑色的瓷砖、红色的瓷砖、小林的初始位置。

【输出格式】
输出一行一个整数,表示小林从初始位置出发经过的黑色瓷砖数。

输入样例:

11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........

输出样例:59

【参考题解】

#include<iostream>
using namespace std;

const int N=55;
int h,w,ans=0;
char a[N][N];
int dis[][2]={{-1, 0},{ 0,-1},{ 1, 0},{ 0, 1}};

void print(){
    for(int i=0; i<h; i++) printf("%s\n", a[i]);
    printf("\n");
}

bool in(int x, int y){
    if(x>=0 && x<w && y>=0 && y<h) return 1;
    return 0;
}

void dfs(int x, int y){
    a[x][y]='#'; ans++; //进入搜索,计数器 +1
    for(int i=0; i<4; i++){
        int tx=x+dis[i][0];
        int ty=y+dis[i][1];
        if(in(tx,ty) && a[tx][ty]=='.'){
            dfs(tx,ty); //满足搜索条件
        }
    }
}

int main(){
//    freopen("data.in", "r", stdin);
//    freopen("data.out", "w", stdout);
    scanf("%d%d",&h,&w); // w 行 h 列
    for(int i=0; i<w; i++) scanf("%s", a[i]);
    for(int i=0; i<w; i++){
        for(int j=0; j<h; j++){
            if(a[i][j]=='@'){
                dfs(i,j); break;//只有一个起点,搜索后直接退出
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

马的遍历2

【题目描述】
中国象棋的规则是马走“日”,现在有一个 8*8 的棋盘,某个格子里放有一个马。
现在指定一个目标点, 你的任务是判定在给定步数内能否移动这个马到指定的目标点。
注意:马不能走到棋盘外的点,因此靠近边界的马并不能到达所有的点,棋盘上的点横纵坐标均从 1 计数。

【输入格式】
第一行两个坐标 x,y,c,(x,y)表示马的起始位置,c 表示最多走几步
第二行两个坐标 a,b,表示马将要到达的位置

【输出格式】
输出一行 yes 或 no 表示可以或不可以实现

输入样例1:

1 1 1
2 3

输出样例1:yes

输入样例2:

1 1 3
8 8

输出样例2:no

测试点数据范围:
对于50%的数据: 1 <= c <= 5;
对于100%的数据: 1 <= c <= 25;

【参考题解】

#include<iostream>
#include<queue>
using namespace std;
const int N=30;
int vis[N][N];
int dis[][2]={
{-2, -1},{-1,-2},{ 1,-2},{ 2,-1},
{ 2,  1},{ 1, 2},{-1, 2},{-2, 1}};

bool in(int x,int y){
    if(x>=1 && x<=8 && y>=1 && y<=8) return 1;
    return 0;
}

void print(){
    for(int i=1; i<=8; i++){
        for(int j=1; j<=8; j++){
            printf("%d", vis[i][j]);
        }printf("\n");
    }printf("\n");
}

void dfs(int x, int y, int step){
    vis[x][y]=step+1;
//    print(); //输出调试
    if(step<=0) return; // 0步特殊处理
    for(int i=0; i<8; i++){
        int tx=x+dis[i][0];
        int ty=y+dis[i][1];// 如果当前步数过小,就被更新
        if(in(tx,ty) && step>0 && vis[tx][ty]<step+1){
            dfs(tx, ty, step-1);
        }
    }
}

int main(){
    freopen("horse.in", "r", stdin);
    freopen("horse.out", "w", stdout);

    int x,y,c,a,b;
    cin>>x>>y>>c>>a>>b;

    dfs(x,y,c);
    if(vis[a][b]) cout<<"yes";
    else cout<<"no";
    return 0;
}
posted @ 2021-12-12 10:20  HelloHeBin  阅读(237)  评论(0编辑  收藏  举报