从起点到终点,每一条路径进行尝试,走不到则回头重新换路,直到走到想要到达的终点,如果所有路线都走不到,在会一直回退到起点。
递归
题目
如图 1所示,3*3 的格子中填写了一些整数。
我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是 60。
本题的要求就是请你编程判定:对给定的 m*n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0。
输入格式
程序先读入两个整数 m,n 用空格分割 (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
提示
第二个用例中:
时限 5 秒, 64M。蓝桥杯 2013 年第四届省赛
代码
#includeusing 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; }
题目
小明冒充 X 星球的骑士,进入了一个奇怪的城堡。
城堡里边什么都没有,只有方形石头铺成的地面。
假设城堡地面是 n* n 个方格。如图所示。
按习俗,骑士要从西北角走到东南角。
可以横向或纵向移动,但不能斜着走,也不能跳跃。
每走到一个新方格,就要向正北方和正西方各射一箭。
(城堡的西墙和北墙内各有 n 个靶子)
同一个方格只允许经过一次。但不必做完所有的方格。
如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?
有时是可以的,比如如图中的例子。
本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)
输入格式
第一行一个整数 N(0
第二行 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 年第七届国赛
代码
#includeusing 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; }
深度优先算法的常规解题思路:
在主函数中有一个起点的递归函数入口。
进入递归函数之后,先设置出口,就是什么情况下你要退回到上一个节点,什么情况下你找到了最终的结果程序结束,输出结果。
然后在递归函数中当目前情况无法满足想要的结果,就要在for框架下进行位置移动,调用递归函数,知道满足上面的条件。
基本模板:
都必须具有标志和位置的声明
bool visit[N][N]; //是否走到过某个方格 int direct[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; //走路移动的方向
都必须具备递归函数的入口
dfs(0,0); // 从(0,0)开始 find(1,1,1,number[1][1]); //图的起点 从第一个位置开始进入函数调用的入口
都必须具有位置移动的框架
for(int i=0;i<4;i++){ //位置的移动 int r=row+direct[i][0]; int c=col+direct[i][1]; ... }
递归函数的基本使用要求:
找到递归函数的出口
找到递归函数的公式
我是哈皮,祝您每天嗨皮,我们下期再见!