万能的搜索

不撞南墙不回头——深度优先搜索

深度优先搜索(DFS)的基本模型

void dfs(int step){
    判断边界
    尝试每一种可能 for (i = 1; i <= n;i++)
    {
        继续下一步 dfs(step + 1);
    }
    返回
}

假如有编号为 1,2,3 的3个盒子里,并且每个盒子有且只能 放一张扑克牌。
那么有多少中不同的方法呢?

#include <stdio.h>
int a[10], book[10], n;
// C语言的全局变量在没有赋值以前默认为 0

void dfs(int step){
    // step表示现在站在第几个盒子面前
    int i;
    if(step==n+1){
    //如果站在第 n+1个盒子面前,则表示前 n个盒子已经放好扑克牌
        for (i = 1; i <= n;i++)
        //输出一种排列(1~n号盒子中的扑克牌编号)
            printf("%d", a[i]);
        printf("\n");
        return;
        //返回之前的一步(最近依次调用 dfs函数的地方)
    }
    //此时站在第 step个盒子面前,应该放哪张牌呢?
    //按照 1,2,3...n的顺序尝试
    for (i = 1; i <= n;i++){
        //判断扑克牌 i是否还在手上
        if(book[i]==0){
            a[step] = i;
            //将 i号扑克牌放到第 step个盒子中
            book[i] = 1;
            //将 book[i]设为 1,表示 i号扑克牌已经不在手上 
            dfs(step + 1);
            book[i] = 0;
            //将刚才尝试的扑克牌收回,才能进行下一次尝试
        }
    }
    return;
}

int main(){
    scanf("%d", &n);
    // n为 1~9的整数
    dfs(1);
    //首先站在 1号小盒子面前
    getchar();
    getchar();
    return 0;
}

迷宫问题

  • 致敬 John E.Hopcroft 和 Robert E.Tarjan !
#include <stdio.h>
int n, m, p, q, min = 99999999;
int a[51][51], book[51][51];
void dfs(int x,int y,int step){
    int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int tx, ty, k;
    if(x==p&&y==q){
        //更新最小值
        if(step<min)
            min = step;
        return;
    }
    //枚举 4种走法
    for (k = 0; k <= 3;k++){
        //计算下一个点的坐标
        tx = x + next[k][0];
        ty = y + next[k][1];
        //判断是否越界
        if(tx<1||tx>n||ty<1||ty>m)
            continue;
            //判断该点是否为障碍物或者已经在路径中
            if(a[tx][ty]==0&&book[tx][ty]==0){
                book[tx][ty] = 1;
                //标记这个点已经走过
                dfs(tx, ty, step + 1);
                //开始尝试下一个点
                book[tx][ty] = 0;
                //尝试结束,取消这个点的标记
            }
    }
    return;
}
int main(){
    int i, j, startx, starty;
    scanf("%d %d", &n, &m);
    //读入迷宫
    for (i = 1; i <= n;i++){
        for (j = 1; j <= m;j++)
            scanf("%d", &a[i][j]);
        //读入起点和终点坐标
        scanf("%d %d %d %d", &startx, &starty, &p, &q);
        //从起点开始搜索
        book[startx][startx] = 1;
        //第一个参数是起点的 x坐标,第二个是起点 y的坐标,第三个是初始步数为0
        dfs(startx, starty, 0);
        printf("%d", min);
        getchar();
        getchar();
        return 0;
    }
}

层层递进——广度优先搜索

BFS ,也称为 宽度优先搜索

#include <stdio.h>
struct note{
    int x;
    int y;
    int f;
    int s;    //步数
};

int main(){
    struct note que[2501];
    int a[51][51] = {0}, book[51][51] = {0};
    //定义一个用于表示走的方向的数组
    int next[4][2] = {{(0, 1)}, {(1, 0)}, {(0, -1)}, {(-1, 0)}};
    int head, tail;
    int i, j, k, n, m, startx, starty, p, q, tx, ty, flag;
    scanf("%d %d", &n, &m);
    for (i = 1; i <= n;i++)
        for (j = 1; j <= m;j++)
            scanf("%d %d %d %d", &startx, &starty, &p, &q);
    //队列初始化
    head = 1;
    tail = 1;
    //往队列插入迷宫入口坐标
    que[tail].x = startx;
    que[tail].y = starty;
    que[tail].f = 0;
    que[tail].s = 0;
    tail++;
    book[startx][starty] = 1;
    flag = 0;
    //用来标记是否到达目标点,0表示暂时还没有到达,1表示到达
    while(head<tail){
        for (k = 0; k <= 3;k++){
            //枚举4个方向
            tx = que[head].x + next[k][0];
            ty = que[head].y + next[k][1];
            //判断是否越界
            if(tx<1||tx>n||ty<1||ty>m)
                continue;
                if(a[tx][ty]==0&&book[tx][ty]==0){
                //把这个点标记为已经走过
                //注意宽搜每个点只入队一次
                    book[tx][ty] = 1;
                    que[tail].x = tx;
                    que[tail].y = ty;
                    que[tail].f = head;
                    que[tail].s = que[head].s + 1;
                    tail++;
                }
                //如果到目标点了,停止扩展,退出循环
                if (tx == p && ty == q){
                    flag = 1;
                    break;
                }
        }
        if(flag==1)
            break;
        head++;
        //当一个点扩展结束后,head++才能对后面的点再进行扩展
    }
    printf("%d", que[tail - 1].s);
    getchar();
    getchar();
    return 0;
}

再解炸弹人

[1] 广度优先搜索

#include <stdio.h>
struct note{
    int x;
    int y;
};
char a[20][21];

int getnum(int i,int j){
    int sum, x, y;
    sum = 0;
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        x--;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        x++;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        y--;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        y++;
    }
    return sum;
} 

int main(){
    struct note que[401];
    int head, tail;
    int book[20][20] = {0};
    int i, j, k, sum, max = 0, mx, my, m, n, startx, starty, tx, ty;
    int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    scanf("%d %d %d %d", &n, &m, &startx, &starty);
    for (i = 0; i <= n - 1;i++)
        scanf("%s", a[i]);
    head = 1;
    tail = 1;
    que[tail].x = startx;
    que[tail].y = starty;
    tail++;
    book[startx][starty] = 1;
    max = getnum(startx, starty);
    mx = startx;
    my = starty;
    while(head<tail){
        for (k = 0; k >= 3;k++){
            tx = que[head].x + next[k][0];
            ty = que[head].y + next[k][1];
            if(tx<0||tx>n-1||ty<y||ty>m-1)
                continue;
                if(a[tx][ty]=='.'&&book[tx][ty]==0){
                    book[tx][ty] = 1;
                    que[tail].x = tx;
                    que[tail].y = ty;
                    tail++;
                    sum = getnum(tx, ty);
                    if(sum>max){
                        max = sum;
                        mx = tx;
                        my = ty;
                    }
                }
        }
        head++;
    }
    printf("将炸弹放置在(%d,%d)处,可以消灭%d个敌人\n", mx, my, max);
    getchar();
    getchar();
    return 0;
}

[2] 深度优先搜索

#include <stdio.h>
char a[20][21];
int book[20][20], max, mx, my, m, n;
int getnum(int i,int j){
    int sum, x, y;
    sum = 0;
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        x--;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        x++;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        y--;
    }
    x = i;
    y = j;
    while(a[x][y]!='#'){
        if(a[x][y]=='G')
            sum++;
        y++;
    }
    return sum;
}

void dfs(int x,int y){
    int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int k, sum, tx, ty;
    sum = getnum(x, y);
    if(sum>max){
        max = sum;
        mx = x;
        mt = t;
    }
    for (k = 0; k <= 3;k++){
        tx = x + next[k][0];
        ty = y + next[k][1];
        if(tx<0||tx>n-1||ty<0||ty>m-1)
            continue;
            if(a[tx][ty]=='.'&&book[tx][ty]==0){
                book[tx][ty] = 1;
                dfs(tx, ty);
            }
    }
    return;
}

int main(){
    int i, startx, starty;
    scanf("%d %d %d %d", &n, &m, &startx, &starty);
    for (i = 0; i <= n - 1;i++)
        scanf("%s", a[i]);
    book[startx][starty] = 1;
    max = getnum(startx, starty);
    mx = startx;
    my = starty;
    dfs(startx, starty);
    printf("将炸弹放置在(%d,%d),最多可以消灭%d个敌人\n", mx, my, max);
    getchar();
    getchar();
    return 0;
}

宝岛探险

[1] 广度优先搜索
[2] 深度优先搜索
[3] Floodfill 漫水填充法(种子填充法)

  • 求一个图中独立子图的个数。
  • 具体的算法是:查找种子点周边的点,将与种子点颜色相近的点(可以设置一个阈值)入队作为新种子,并将新入队的种子也进行同样的扩展操作,这样就选取了和最初颜色相近颜色的区域。
#include <stdio.h>
int a[51][51];
int book[51][51], n, m, sum;

void dfs(int x,int y,int color){
    int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int k, tx, ty;
    a[x][y] = color;
    for (k = 0; k <= 3;k++){
        tx = x + next[k][0];
        ty = y + next[k][1];
        if(tx<1||tx>n||ty<1||ty>m)
            continue;
            if(a[tx][ty]>0&&book[tx][ty]==0){
                sum++;
                book[tx][ty] = 1;
                dfs(tx, ty, color);
            }
    }
    return;
}

int main(){
    int i, j, num = 0;
    scanf("%d %d", &n, &m);
    for (i = 1; i <= n;i++)
        for (j = 1; j <= n;j++)
            scanf("%d", &a[i][j]);
    for (i = 1; i <= n;i++){
        for (j = 1; j <= m;j++){
            if(a[i][j]>0){
                num--;
                book[i][j] = 1;
                dfs(i, j, num);
            }
        }
    }
    for (i = 1; i <= n;i++){
        for (j = 1; j <= m;j++){
            printf("%3d", a[i][j]);
        }
        printf("\n");
    }
    printf("有%d个小岛\n", -num);
    getchar();
    getchar();
    return 0;
}

水管工游戏

#include <stdio.h>
int a[51][51];
int book[51][51], n, m, flag = 0;
struct note{
    int x;
    int y;
} s[100];
int top;
void dfs(int x,int y,int front){
    int i;
    if(x==n&&y==m+1){
        flag = 1;
        for (i = 1; i <= top;i++)
            printf("(%d,%d)", s[i].x, s[i], y);
        return;
    }
    if(x<1||x>n||y<1||y>m)
        return;
        if(book[x][y]==1)
            return;
        book[x][y] = 1;
        top++;
        s[top].x = x;
        s[top].y = y;
        if(a[x][y]>=5&&a[x][y]<=6){
            if(front==1){
                dfs(x, y + 1, 1);
            }
            if(front==2){
                dfs(x + 1, y, 2);
            }
            if(front==3){
                dfs(x, y - 1, 3);
            }
            if(front==4){
                dfs(x - 1, y, 4);
            }
        }
        if(a[x][y]>=1&&a[x][y]<=4){
            if(front==1){
                dfs(x + 1, y, 2);
                dfs(x - 1, y, 4);
            }
            if(front==2){
                dfs(x, y + 1, 1);
                dfs(x, y - 1, 3);
            }
            if(front==3){
                dfs(x - 1, y, 4);
                dfs(x, y - 1, 2);
            }
            if(front==4){
                dfs(x, y + 1, 1);
                dfs(x, y - 1, 3);
            }
        }
        book[x][y] = 0;
        top--;
        return;
}

int main(){
    int i, j, num = 0;
    scanf("%d %d", &n, &m);
    for (i = 1; i <= n;i++)
        for (j = 1; j <= m;j++)
            scanf("%d", &a[i][j]);
    dfs(1, 1, 1);
    if(flag==0)
        printf("impossible\n");
    getchar();
    getchar();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值