深度优先算法——蓝桥杯省赛算法

深度优先搜索——蓝桥杯省赛

主要原理

从起点到终点,每一条路径进行尝试,走不到则回头重新换路,直到走到想要到达的终点,如果所有路线都走不到,在会一直回退到起点。

主要工具

递归

例题讲解1

题目

如图 1所示,3*3 的格子中填写了一些整数。

深度优先算法——蓝桥杯省赛算法_第1张图片

我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是 60

本题的要求就是请你编程判定:对给定的 m*n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。

如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。

如果无法分割,则输出 0

输入格式

程序先读入两个整数 mn 用空格分割 (m,n<10) 表示表格的宽度和高度。

接下来是 n 行,每行 m 个正整数,用空格分开。每个整数不大于 10000

输出格式

程序输出:在所有解中,包含左上角的分割区可能包含的最小的格子数目。

样例

样例输入 #1

 3 3
 10 1 52
 20 30 1
 1 2 3

样例输出 #1

 3

样例 #2

样例输入 #2

 4 3
 1 1 1 1
 1 30 80 2
 1 1 1 100

样例输出 #2

 10

提示

第二个用例中:

深度优先算法——蓝桥杯省赛算法_第2张图片

时限 5 秒, 64M。蓝桥杯 2013 年第四届省赛

代码

 #include
 using namespace std;
 ​
 //搜索算法 
 ​
 int n;
 int m;
  
 int ans;   //最终的格子数结果 
 int number[11][11];
 bool vis[11][11];   //默认为0 false 用于标志你这个位置是否走过 便于后退
 ​
 int sum;   //整个格子的总和  因为要对半分,只要从1,1开始搜查相加和为sum总和的一半 
 int direct[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
 //分别对应右走一步, 左走一步, 上, 下
 ​
 ​
 void find(int x, int y, int tmpans, int tmpsum)  {
     // tmpans 每一次搜索的时候对于格子数的记录
     // tmpsum 每一次搜索的和 
     
     if(tmpsum == sum){       //如果某次的搜索和等于总和的一半 
         ans = tmpans;        //则最终的结果就是这次搜索的格子数 
         return;
     } 
     
     if(tmpsum > sum){    //如果搜索的结果大于一半了 表明走超了 退回到上一个 所以直接return
         return;              //返回到上一层 进入ans的判断  如果ans == 0  则把刚刚走入的格子恢复,                              //去走另一个格子 
     } 
     
     //否则就是还没有到总和的一半 所以用递归调用去不断搜索
     for(int i = 0; i < 4; i++){
         //前两步现在水平移动 如果continue了再进行上下移动 
         //(x,y)是目前的格子坐标位置
         int xx = x + direct[i][0];
         int yy = y + direct[i][1];
         if(xx < 1 || xx > n || yy < 1 || yy > m || vis[xx][yy]){  
           //去除超出边界值  去除已经来到过的格子 如果确实是这种非法情况则调换位置后再进入下一格子 
             continue;
         } 
         vis[xx][yy] = 1;      //表示来到过这个格子,已经加进来了 前1 在这条路径线中
          //递归入口
         find(xx, yy, tmpans + 1, tmpsum + number[xx][yy]);
         //递归操作  1是找到出口 2是找到递归的公式
         //这就是递归的出口 只要ans找到了有值了 一路返回到主函数中输出结果
         if(ans != 0){
             return;
         }
         vis[xx][yy] = 0;      //后0  去恢复状态 可能换一条路径还能抵达
     } 
 }
 ​
 int main(){
     //简单的读入数据
     cin >> n >> m;
     for(int i = 1; i <= n; i++){
         for(int j = 1; j <= m; j++){
             cin >> number[i][j];
             sum += number[i][j]; 
         }
     }
     
     if(sum % 2 != 0){      //当总和是奇数的时候直接表示无法分隔
         cout << 0;
         return 0;   
     }
     
     sum /= 2;
     
     find(1,1,1,number[1][1]);        //图的起点 从第一个位置开始进入函数调用的入口 
     
     cout << ans;
     return 0;
 }

例题讲解2

题目

小明冒充 X 星球的骑士,进入了一个奇怪的城堡。

城堡里边什么都没有,只有方形石头铺成的地面。

假设城堡地面是 n* n 个方格。如图所示。

深度优先算法——蓝桥杯省赛算法_第3张图片

按习俗,骑士要从西北角走到东南角。

可以横向或纵向移动,但不能斜着走,也不能跳跃。

每走到一个新方格,就要向正北方和正西方各射一箭。

(城堡的西墙和北墙内各有 n 个靶子)

同一个方格只允许经过一次。但不必做完所有的方格。

如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?

有时是可以的,比如如图中的例子。

本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)

输入格式

第一行一个整数 N(0,表示地面有 N * N 个方格。

第二行 N 个整数,空格分开,表示北边的箭靶上的数字(自西向东)

第三行 N 个整数,空格分开,表示西边的箭靶上的数字(自北向南)

输出格式

一行若干个整数,表示骑士路径。

为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号 :0,1,2,3 \cdots

比如,图中的方块编号为:

 0  1  2  3
 4  5  6  7
 8  9  10 11
 12 13 14 15

样例 #1

样例输入 #1

 4
 2 4 3 4
 4 3 3 3

样例输出 #1

 0 4 5 1 2 3 7 11 10 9 13 14 15

提示

时限 1 秒, 256M。蓝桥杯 2016 年第七届国赛

代码

#include
using namespace std;
#include

 
const int N=25;
int top[N];
int L[N];
bool visit[N][N];
int direct[4][2]={{-1,0},{1,0},{0,-1},{0,1}};  //走路移动的方向 

vector v;      //向量 
int n;
 
bool empty(){
 
    for(int i=0;i=n||c>=n){
        return false;
    }
    //如果visit为真 则表明曾经来到过,已经在自己的路线中 不能重复再走 
    if(visit[r][c]){
        return false;
    }
    //   这条路在箭的数量限制下不能走了 
    if(L[r]<=0||top[c]<=0){
        return false;
    }
    // 经过所有考验 可以到达这个新位置 
    return true;
}

void dfs(int row,int col){       //row行 Column列 
    
	//来到这里就需要射箭 
    L[row]--;
    top[col]--; 
	            
    //因为来到这里 就需要给出标志 
    visit[row][col]=true;
    
	//把这个位置对应的序号记录到向量v中 
    v.push_back(row*n+col);   //直接在数组末端添加 
     
	//  首先士兵的位置需要到达终点的位置 
    if(row==n-1 && col==n-1){
    	//其次墙上的箭 数量全部清空 
        if( empty() ){
        	//最后输出真正的最终结果 
            for(int i=0;i>n;
    //上面北方的靶子箭数量 
    for(int i=0;i>top[i];
    }      
    //左侧西方 
    for(int i=0;i>L[i];
    }
    //递归函数的入口 
    dfs(0,0);    // 从(0,0)开始 
    
    return 0;
}

总结

深度优先算法的常规解题思路:

  1. 在主函数中有一个起点的递归函数入口。

  2. 进入递归函数之后,先设置出口,就是什么情况下你要退回到上一个节点,什么情况下你找到了最终的结果程序结束,输出结果。

  3. 然后在递归函数中当目前情况无法满足想要的结果,就要在for框架下进行位置移动,调用递归函数,知道满足上面的条件。

基本模板:

  1. 都必须具有标志和位置的声明

    bool visit[N][N];    //是否走到过某个方格
    int direct[4][2]={{-1,0},{1,0},{0,-1},{0,1}};  //走路移动的方向 

  2. 都必须具备递归函数的入口

     dfs(0,0);    // 从(0,0)开始 
    
     find(1,1,1,number[1][1]);        //图的起点 从第一个位置开始进入函数调用的入口 

  3. 都必须具有位置移动的框架

     for(int i=0;i<4;i++){
        	//位置的移动 
            int r=row+direct[i][0];
            int c=col+direct[i][1];
            ...
     }

递归函数的基本使用要求:

  1. 找到递归函数的出口

  2. 找到递归函数的公式

我是哈皮,祝您每天嗨皮,我们下期再见!

你可能感兴趣的:(蓝桥杯,算法,深度优先)