[递归回溯]连接卡片最短路径

这篇具有很好参考价值的文章主要介绍了[递归回溯]连接卡片最短路径。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

小游戏

题目描述

   一天早上,你起床的时候想:“我编程序这么牛,为什么不能靠这个挣点银子呢?”因此你决定编写一个小游戏。
   游戏在一个分割成w * h个长方格子的矩形板上进行。如图所示,每个长方格子上可以有一张游戏卡片,也可以没有。当下面的情况满足时,我们认为两个游戏卡片之间有一条路径相连:
   路径只包含水平或者竖直的直线段。路径不能穿过别的游戏卡片。但是允许路径临时离开矩形板。
   下面是一个例子:

[递归回溯]连接卡片最短路径,算法

这里在(1, 3)和(4, 4)处的游戏卡片是可以相连的。而在 (2, 3) 和 (3, 4) 处的游戏卡是不相连的,因为连接他们的每条路径都必须要穿过别的游戏卡片。
   你现在要在小游戏里面判断是否存在一条满足题意的路径能连接给定的两个游戏卡片。

关于输入

   输入包括多组数据。一个矩形板对应一组数据。每组数据的第一行包括两个整数w和h (1 <= w, h <= 75),分别表示矩形板的宽度和高度。下面的h行,每行包括w个字符,表示矩形板上的游戏卡片分布情况。使用‘X’表示这个地方有一个游戏卡片;使用空格表示这个地方没有游戏卡片。之后的若干行,每行包括4个整数x1, y1, x2, y2 (1 <= x1, x2 <= w, 1 <= y1, y2 <= h),表示两个卡片在矩形板上的位置(注意:矩形板左上角的坐标是(1, 1))。如果一行上有4个0,则表示这组测试数据的结束。测试数据保证这两个游戏卡片所处的位置是不相同的。
   如果一行上有2个0,那么表示所有的输入结束了。

关于输出

   对每一个矩形板,输出一行“Board #n:”,这里n是输入数据的编号(从1开始)。然后对于每一对需要测试的游戏卡片输出一行。这一行的开头是“Pair m: ”,这里m是测试卡片的编号(对每个矩形板,编号都从1开始)。接下来,如果可以相连,则找到连接这两个卡片的所有路径中包括线段数最少的路径,输出“k segments.”,这里k即是找到的最优路径中包括的线段的数目;如果不能相连,则输出“impossible.”。
   注意:每个矩形板之后须输出一个空行。

例子输入
5 4
XXXXX
X   X
XXX X
 XXX
2 3 5 3
1 3 4 4
2 3 3 4
0 0 0 0
4 4
XXXX
XXXX
XXXX
XXXX
1 1 2 1
2 2 3 2
1 1 3 1
3 4 4 3
2 1 2 4
1 1 2 2
0 0 0 0
0 0
例子输出
Board #1:
Pair 1: 4 segments.
Pair 2: 3 segments.
Pair 3: impossible.
Board #2:
Pair 1: 1 segments.
Pair 2: 1 segments.
Pair 3: 3 segments.
Pair 4: 4 segments.
Pair 5: 5 segments.
Pair 6: impossible.
解题分析

我们可以设计一个探险家找宝藏的程序,利用dfs和剪枝的策略去寻找最短路径,然而,其实本题使用bfs会比较好,不过我们暂时用dfs来做。

首先,你可以把这个程序想象成一个探险家在一个迷宫中寻找宝藏。这个迷宫就是我们的矩形板,而探险家的任务就是找到两个游戏卡片之间的最短路径。

探险家有一个地图(board数组),这个地图上标记了所有的游戏卡片的位置。探险家还有一个记事本(visited数组),用来记录他已经走过的位置,以防止他在迷宫中迷路。探险家还有一个指南针(dx和dy数组),告诉他可以向哪些方向移动。

当探险家开始他的探险时,他首先会检查他是否已经找到了宝藏(也就是目标游戏卡片)。如果找到了,他就会记录下这条路径的长度,并且和他之前找到的最短路径进行比较,如果这条路径更短,他就会记下这条路径。如果没有找到宝藏,他就会沿着四个方向继续探险。在每个方向上,他都会检查这个方向是否可以走(也就是这个位置没有游戏卡片并且没有被访问过)。如果可以走,他就会标记这个位置已经被访问过,然后继续探险。如果这个方向和他之前的方向不同,那么他就会把路径长度加1,因为他需要转弯。当他探险结束后,他会把这个位置的访问标记清除,因为他可能会从其他路径再次访问这个位置。

这个程序的关键是深度优先搜索算法。这个算法的思想就是"深入"探索每一个可能的路径,直到找到目标或者无路可走。在这个过程中,程序使用了访问标记数组来避免重复访问,使用了路径长度来剪枝,以提高搜索效率。

在主函数中,程序处理了多组测试数据。对于每一组数据,程序都会读入矩形板的大小和游戏卡片的分布情况,然后对于每一对需要测试的游戏卡片,程序都会进行深度优先搜索。搜索结束后,程序会输出最短路径长度或者"impossible"。

总的来说,这个程序就像一个探险家在迷宫中寻找宝藏,他会尽可能的寻找最短的路径,如果找不到路径,他就会告诉我们"impossible"。

代码实现

(纯享版)

#include <stdio.h>
#include <string.h>
#define min(x,y) x>y?y:x

int w,h,x1,y1,x2,y2;
char board[80][80];
int visited[80][80]={0};
int minpath=1e9;
const int dx[]={-1,0,1,0};
const int dy[]={0,-1,0,1};
int countBoard=0;

void dfs(int x,int y,int len,int pos){
    if(len>=minpath){
        return;
    }
    if(x==x2 && y==y2){
        minpath=min(minpath,len);
        return;
    }
    for(int i=0;i<4;i++){
        int nx=x+dx[i],ny=y+dy[i];
        if(nx>=0 && nx<=w+1 && ny>=0 && ny<=h+1 &&((board[nx][ny]==' ' && visited[nx][ny]==0)||(nx==x2 && ny==y2))){
            visited[nx][ny]=1;
            if(pos==i){
                dfs(nx,ny,len,i);
            }
            else{
                dfs(nx,ny,len+1,i);
            }
            visited[nx][ny]=0;
        }
    }
}

int main(void) { 
	while(scanf("%d%d",&h,&w)){
	    getchar();
	    if(h==0 && w==0) break;
	    for(int i=0;i<=w+1;i++){
	        for(int j=0;j<=h+1;j++){
	            if(i==0 || j==0 || i==w+1 || j==h+1){
	                board[i][j]=' ';
	            }
	            else
	            scanf("%c",&board[i][j]);
	        }
	        if(i>=1 && i<=w){
	            getchar();
	        }
	    }
	    int countPair=0;
	    countBoard++;
	    printf("Board #%d:\n",countBoard);
	    while(scanf("%d%d%d%d",&y1,&x1,&y2,&x2)){
	        countPair++;
	        if(x1==0 && y1==0 && x2==0 && y2==0){
	            break;
	        }
	        minpath=1e9; memset(visited,0,sizeof(visited));
	        dfs(x1,y1,0,-1);
	        if(minpath!=1e9)
	        printf("Pair %d: %d segments.\n",countPair,minpath);
	        else
	        printf("Pair %d: impossible.\n",countPair);
	    }
	}
	return 0;
}

(详细注释版)

#include <stdio.h>
#include <string.h>  // 引入标准输入输出和字符串处理函数库

#define min(x,y) x>y?y:x  // 定义求最小值的宏

// 定义全局变量
int w,h,x1,y1,x2,y2;
char board[80][80];
int visited[80][80]={0};
int minpath=1e9;
const int dx[]={-1,0,1,0};
const int dy[]={0,-1,0,1};
int countBoard=0;

// 定义深度优先搜索函数
void dfs(int x,int y,int len,int pos){
    // 如果当前路径长度大于等于最短路径长度,结束搜索
    if(len>=minpath){
        return;
    }
    // 如果当前位置是目标位置,更新最短路径长度,然后结束搜索
    if(x==x2 && y==y2){
        minpath=min(minpath,len);
        return;
    }
    // 在四个方向上进行搜索
    for(int i=0;i<4;i++){
        // 计算新的位置
        int nx=x+dx[i],ny=y+dy[i];
        // 检查新的位置是否在矩形板内,是否没有游戏卡片,以及是否没有被访问过
        if(nx>=0 && nx<=w+1 && ny>=0 && ny<=h+1 &&((board[nx][ny]==' ' && visited[nx][ny]==0)||(nx==x2 && ny==y2))){
            // 标记新的位置已经被访问过
            visited[nx][ny]=1;
            // 如果新的方向和当前的方向相同,路径长度不变,否则路径长度加1
            if(pos==i){
                dfs(nx,ny,len,i);
            }
            else{
                dfs(nx,ny,len+1,i);
            }
            // 搜索结束后,清除新的位置的访问标记
            visited[nx][ny]=0;
        }
    }
}

int main(void) { 
    // 读入矩形板的高度和宽度
    while(scanf("%d%d",&h,&w)){
        getchar();
        // 如果高度和宽度都是0,结束循环
        if(h==0 && w==0) break;
        // 读入矩形板上的游戏卡片分布情况
        for(int i=0;i<=w+1;i++){
            for(int j=0;j<=h+1;j++){
                // 如果位置在矩形板的边界上,设置为没有游戏卡片,否则读入游戏卡片的分布情况
                if(i==0 || j==0 || i==w+1 || j==h+1){
                    board[i][j]=' ';
                }
                else
                scanf("%c",&board[i][j]);
            }
            if(i>=1 && i<=w){
                getchar();
            }
        }
        // 初始化游戏卡片的计数器,增加矩形板的计数器,然后输出矩形板的编号
        int countPair=0;
        countBoard++;
        printf("Board #%d:\n",countBoard);
        // 读入需要测试的游戏卡片的位置
        while(scanf("%d%d%d%d",&y1,&x1,&y2,&x2)){
            countPair++;
            // 如果游戏卡片的位置都是0,结束循环
            if(x1==0 && y1==0 && x2==0 && y2==0){
                break;
            }
            // 初始化最短路径长度,清除所有的访问标记,然后开始深度优先搜索
            minpath=1e9; memset(visited,0,sizeof(visited));
            dfs(x1,y1,0,-1);
            // 如果找到了路径,输出路径长度,否则输出"impossible"
            if(minpath!=1e9)
            printf("Pair %d: %d segments.\n",countPair,minpath);
            else
            printf("Pair %d: impossible.\n",countPair);
        }
    }
    return 0;  // 程序结束
}

当然,如果用bool数组去代替棋盘 用01对局面上是否有游戏卡牌进行表达也是一个不错的选择。文章来源地址https://www.toymoban.com/news/detail-755112.html

#include <iostream>
#include <cstring>
using namespace std;

int w=1,h=1;    bool board[80][80]={0};
const int dx[]={0,1,0,-1};   const int dy[]={-1,0,1,0};
bool is[80][80]={0}; 	int x1,y1,x2,y2; int ans,countboard=0;

void dfs(int x,int y,int d,int step){
    if(step>=ans) return;
	if(x==x2 && y==y2){
		ans=min(ans,step);
		return;
	}
	for(int i=0;i<4;i++){
		int nx=x+dx[i],ny=y+dy[i];
		if(nx>=0 && nx<=w+1 && ny>=0 && ny<=h+1 && ((is[ny][nx]==0 && board[ny][nx]==0 )|| (ny==y2 && nx==x2))){
			is[ny][nx]=1;
			if(d==i) dfs(nx,ny,i,step);
			else dfs(nx,ny,i,step+1);
			is[ny][nx]=0;
		}
	}
	return;
}

int main() {
	while(scanf("%d%d",&w,&h)){
		getchar();
	    if(w==0 && h==0) break;
		countboard++;
		printf("Board #%d:\n",countboard);
		char c;
		for(int i=0;i<=h+1;i++){
			for(int j=0;j<=w+1;j++){
			    if(i==0 || j==0 || i==h+1 || j==w+1){
			        board[i][j]=0;
			        continue;
			    }
				scanf("%c",&c);
				if(c=='X') board[i][j]=1;
				else board[i][j]=0;
				if(j==w) getchar();
			}
		}
		int countpair=0;
		while(scanf("%d%d%d%d",&x1,&y1,&x2,&y2)){
			if(x1==0 && y1==0 && x2==0 && y2==0) break;
			countpair++;
			ans=1e9;
			dfs(x1,y1,-1,0);
			if(ans!=1e9)
			printf("Pair %d: %d segments.\n",countpair,ans);
			else printf("Pair %d: impossible.\n",countpair);
		}
	}
	return 0;
}

到了这里,关于[递归回溯]连接卡片最短路径的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法系列篇】递归、搜索和回溯(二)

    前面为大家介绍了关于递归的知识,以及使用递归解决了几个问题,那么这篇文章将带大家巩固一下关于递归的知识。 https://leetcode.cn/problems/swap-nodes-in-pairs/description/ 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。 你必须在不修改节点内部的值的情况

    2024年02月05日
    浏览(31)
  • 递归、搜索与回溯算法(专题二:深搜)

    往期文章(希望小伙伴们在看这篇文章之前,看一下往期文章) (1)递归、搜索与回溯算法(专题零:解释回溯算法中涉及到的名词)【回溯算法入门必看】-CSDN博客 (2)递归、搜索与回溯算法(专题一:递归)-CSDN博客  深搜是实现递归的一种方式,接下来我们之间从题

    2024年01月20日
    浏览(73)
  • 算法与数据结构——递归算法+回溯算法——八皇后问题

    八皇后问题是一个经典的回溯算法问题,目的是在8×8的国际象棋棋盘上放置八个皇后,使得没有皇后可以互相攻击(即没有两个皇后在同一行、同一列或同一对角线上)。 回溯算法是一种解决问题的算法,它通过尝试所有可能的解决方案来解决问题。在八皇后问题中,计算

    2024年02月09日
    浏览(45)
  • 算法 矩阵最长递增路径-(递归回溯+动态规划)

    牛客网: BM61 求矩阵的最长递增路径 解题思路: 1. 遍历二维矩阵每个位置,max求出所有位置分别为终点时的最长路径 2. 求某个位置为终点的最长路径时,使用动态规划dp对已经计算出的位置进行记录 3. 处理某个位置的最长路径时,如果dp[i][j]位置已有值,则直接返回即可,否则

    2024年02月07日
    浏览(34)
  • 递归、搜索与回溯算法(专题六:记忆化搜索)

    目录 1. 什么是记忆化搜索(例子:斐波那契数) 1.1 解法一:递归 1.2 解法二:记忆化搜索 1.2.1 记忆化搜索比递归多了什么? 1.2.2 提出一个问题:什么时候要使用记忆化搜索呢? 1.3 解法三:动态规划 1.3.1 先复习一下动态规划的核心步骤(5个),并将动态规划的每一步对应

    2024年01月20日
    浏览(40)
  • 【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++)

    后面的练习是接着下面链接中的文章所继续的,在对后面的题练习之前,可以先将下面的的文章进行了解👇: 【算法】{画决策树 + dfs + 递归 + 回溯 + 剪枝} 解决排列、子集问题(C++) 思路 题意分析 :要求根据给出的数字,算出合法的括号组成个数。根据题目,我们可以总

    2024年02月22日
    浏览(38)
  • 【C++】递归,搜索与回溯算法入门介绍和专题一讲解

    个人主页:🍝在肯德基吃麻辣烫 我的gitee:C++仓库 个人专栏:C++专栏 从本文开始进入递归,搜索与回溯算法专题讲解。 递归就是函数自己调用自己。 递归的本质是: 主问题:—相同的子问题 子问题:—相同的子问题 通过: 1)通过递归的细节展开图(前期可以,过了前期

    2024年02月09日
    浏览(28)
  • 算法思想—枚举、递推、迭代、递归、分治、贪心、动态规划、回溯、模拟、分支定界

    算法思想 枚举(暴力算法) 枚举算法(暴力算法)是一种通过逐一尝试所有可能解来解决问题的算法。它的基本思想是将问题的所有可能答案一一列举出来,并根据一定的判断条件来确定哪些答案是合适的。这种算法通常使用循环来实现,因为需要尝试所有可能的情况。两

    2024年02月01日
    浏览(27)
  • 【算法】回溯:与递归,dfs的同质与分别,剪枝与恢复现场的详细理解,n皇后的回溯解法及算法复杂度分析。

    目录 ​编辑 1.什么是回溯 2.关于剪枝 3.关于恢复现场 4.题目:二叉树的所有路径(凸显恢复现场:切实感受回溯与深搜) 问题分析 ①函数设置为:void Dfs(root) ②函数设置为:void Dfs(root,path) 解题思想:使⽤深度优先遍历(DFS)求解。 代码实现 5.N后问题 问题分析 4皇后的放置

    2024年04月16日
    浏览(29)
  • Java中常用算法及示例-分治、迭代、递归、递推、动态规划、回溯、穷举、贪心

    1、分治算法的基本思想是将一个计算复杂的问题分成规模较小、计算简单的小问题求解, 然后综合各个小问题,得到最终答案。 2、穷举(又称枚举)算法的基本思想是从所有可能的情况中搜索正确的答案。 3、迭代法(Iterative Method) 无法使用公式一次求解,而需要使用重复结构

    2024年02月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包