算法课(递归)
迷宫求解
一天早上,你起床的时候想:“我编程序这么牛,为什么不能靠这个赚点小钱呢?”因此你决定编写一个小游戏。
游戏在一个分割成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,表示这组测试数据的结束。
如果一行上给出w = h = 0,那么表示所有的输入结束了。
【输出】
对每一个矩形板,输出一行“Board #n:”,这里n是输入数据的编号。然后对每一组需要测试的游戏卡片输出一行。这一行的开头是“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 0 0
输出:
Board #1: Pair 1: 4 segments. Pair 2: 3 segments. Pair 3: impossible.
这就是一个典型的迷宫求解问题,首先,我们要做的是在两点间尝试找一条可行路径(当然也可能不可行),所谓可行路径是除起点和终点不经过其他”X“点,这个题要一个特殊要求,可以走外面;
那么我们把环绕迷宫一圈都赋值为可行,在搜索的过程中,一个点东西南北不断试一直试到终点或者遇到一个不可试点返回上一步(回溯),一个点可行有两种,一个是不越界且该点为空格且没有被
目前走过,一个是到了终点,每到一个点,都标记一下,dfs后再将标记解除(回溯),每一次搜出一条可行路径后将其更新最小值,最后求得最小值(如果路径可求的话)。注意这里的路径长度为
线段数、
#include <iostream> #include <iostream> #include <algorithm> #include <vector> #include <cstring> using namespace std; char hj[101][101]; //存迷宫图 int minstep,w,h,to[4][2]{{0,1},{1,0},{0,-1},{-1,0}}; //表示东西南北四个方向 bool mark[101][101]; //状态数组 void jk(int cx,int cy,int endx,int endy,int step,int f) // cx , cy 为当前位置,endx,endy为终点,step为路径长度,f为前一次的方向 { if(step>minstep) return ; //如果该条路径长超过现有最值,就可以放弃了这条了,算是一个小小的剪枝 if(cx==endx&&cy==endy) { minstep=min(minstep,step); return ; } for(int i=0;i<4;i++) { int x=cx+to[i][0]; int y=cy+to[i][1]; //从一个点按一个方向走一次后的坐标 if((x>-1)&&(x<w+2)&&(y>-1)&&(y<h+2)&&((((hj[y][x]==' ')&&(mark[y][x]==false))||((x==endx)&&(y==endy)&&hj[y][x]=='X')))) { mark[y][x]=true;//标记为走过 if(f==i) //如果与上一次方向一致,step不更新 { jk(x,y,endx,endy,step,i); } else { jk(x,y,endx,endy,step+1,i); } mark[y][x]=false;//回溯 } } } int main() { int num=0; while(cin >> w >> h) { if(w==0&&h==0) break; num++; cout<<"Board #" <<num <<":" << endl; for(int i=0;i<100;i++) { hj[0][i]=hj[i][0]=' '; } for(int i=1;i<=h;i++) { getchar(); //getchar读入单个字符 for(int j=1;j<=w;j++) { hj[i][j]=getchar(); //读图 } } for(int i=0;i<=w;i++) { hj[h+1][i+1]=' '; } for(int i=0;i<=h;i++) { hj[i+1][w+1]=' '; } int sx,sy,endx,endy,cnt=0; while(cin >> sx >> sy >> endx >> endy&&sx>0 ) { cnt++; minstep=101010; memset(mark,0,sizeof(mark)); jk(sx,sy,endx,endy,0,-1); if(minstep<100000) //按要求输出 { cout << "Pair " << cnt <<": " << minstep <<" segments."<< endl; } else { cout << "Pair " << cnt <<": impossible. " << endl; } }cout << endl; } return 0; }
八皇后
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
【输入】
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)
【输出】
输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串
2 1 92
输出
15863724 84136275
又是一道很经典的八皇后问题,首先每一棋子横竖斜排不能有棋子,只能一个一个试,将八皇后问题的92种解存起来。
#include <iostream> using namespace std; int q[92][9],row[8],num=0; //q数组存八皇后的解,row[i]=j表示第i行的棋子位置在第j列 int n,i,j,h; void queen(int i) //i表示处理到哪一行 { int k; if(i==8) //如果一组解已结束 { for(j=0;j<8;j++) { q[num][j]=row[j];//将这一组解存起来 } num++; //计数器加一 return ; 进入下一组 } else { for(int j=1;j<=8;j++) //从这一行开始枚举每个列 { for( k=0;k<i;k++) { if(row[k]==j||abs(k-i)==abs(row[k]-j)) //判断是否与这组解中其他位置冲突 { break; } } if(k==i) //如果不冲突 { row[k]=j; //存下位置 queen(i+1); // 进行下一行 } } } } int main() { queen(0); cin >> n; while(n--) { cin >> h; for(int i=0;i<8;i++) { cout << q[h-1][i];//直接输出就成了,数组第一维是从0开始的 } cout << endl; } return 0; }
算24点
给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。
这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。
比如,对于5,5,5,1,我们知道5 * (5 – 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。
【输入】
输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
【输出】
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
这是一个也是一个典型的搜索问题,显然我们直接枚举所有情况是不可行的,所以这里要使用dfs来解;
#include<iostream> #include<string.h> #include<bits/stdc++.h> #include<stack> using namespace std; bool dfs(double a[],int x) //表示a数组中的数还要经过x次题目中的运算 { if(x==1&&fabs(a[1]-24)<=1e-6) //浮点数不能直接比较 { return 1; } double b[6]; memset(b,0,sizeof(b));//赋初值0 for(int i=1;i<=x-1;++i) { for(int j=i+1;j<=x;++j) { int k=1; for(int l=1;l<=x;++l) { if(l!=i&&l!=j) //存无关值 { b[k++]=a[l]; } }
//列举所有情况去搜索 b[k]=a[i]+a[j];if(dfs(b,x-1)) return 1; b[k]=a[j]-a[i];if(dfs(b,x-1)) return 1; b[k]=a[i]-a[j];if(dfs(b,x-1)) return 1; b[k]=a[i]*a[j];if(dfs(b,x-1)) return 1; if(a[j]!=0) { b[k]=a[i]/a[j];if(dfs(b,x-1)) return 1; } if(a[i]!=0) { b[k]=a[j]/a[i];if(dfs(b,x-1)) return 1; } } } return 0; } int main() { while(1) { double a[6]; memset(a,0,sizeof(a)); int sum=0; for(int i=1;i<=4;++i) { cin >> a[i]; sum+=a[i]; } if(sum==0) { break; } if(dfs(a,4)) { cout << "YES" << endl; } else { cout << "NO" << endl; } } return 0; }
分解因数
给出一个正整数a,要求分解成若干个正整数的乘积,即a = a1 * a2 * a3 * ... * an,并且1 < a1 <= a2 <= a3 <= ... <= an,问这样的分解的种数有多少。注意到a = a也是一种分解。
输入】
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a (1 < a < 32768)
【输出】
n行,每行输出对应一个输入。输出应是一个正整数,指明满足要求的分解的种数
输入 输出
2 2 1 20 4
递归要将问题一步一步简化解决,fun(a,n)表示因子从a开始n的分解因数。
#include <math.h> #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> #include <map> #include <math.h> #include <set> #include<sstream> #define maxn 100009 #define minn 03xfxfxfxf #define mod 1000000007 using namespace std; //double dp[101010] int ans; void fun(int a,int n) { for(int i=a;i<=n;i++) { if(n%i==0&&i<=n/i) //如果找到了一个因子i { ans++;//计数器加一 fun(i,n/i); //n/i为剩下的因子之积,再接着去找这个数分解因数 } if(i>n/i) break; //接下来就是重复情况了,所以跳出结束 } //return 1; } int main(void) { int t,m; cin >> t; while(t--) { cin >> m; ans=1; //自己也是一种分解 fun(2,m); // cout << ans << endl; } return 0; }
逆波兰
逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括+ - * /四个。
【输入】
输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数。
【输出】
输出为一行,表达式的值。
可直接用printf("%f\n", v)输出表达式的值v。
输入
* + 11.0 12.0 + 24.0 35.0
输出
1357.000000
这递归其实很神,%%%,不懂手动模拟一下就懂了,反正我是想不到qaq。
#include<iostream> #include<string.h> #include<bits/stdc++.h> #include<stack> using namespace std; char a[1010]; double fj() { scanf("%s",a); if(a[0]=='+') { return fj()+fj(); //如果是“+”,则将读取到的下一个值与该值得后一个值相加,下面的代码也是类似 } else if(a[0]=='-') { return fj()-fj(); } else if(a[0]=='*') { return fj()*fj(); } else if(a[0]=='/') { return fj()/fj(); } else { return atof(a); //atof可以把字符串转换为浮点数 } } int main() { printf("%f\n",fj()); return 0; }
括号匹配
在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$"标注,不能匹配的右括号用"?"标注.
【输入】
输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100
注意:cin.getline(str,100)最多只能输入99个字符!
【输出】
对每组输出数据,输出两行,第一行包含原始输入字符,第二行由"$","?"和空格组成,"$"和"?"表示与之对应的左括号和右括号不能匹配。
输入
((ABCD(x)
)(rttyy())sss)(
输出
((ABCD(x)
$$
)(rttyy())sss)(
? ?$
括号匹配问题显然一般用栈来做,首先先让左括号的位置入栈,并把它标记,遇到右括号后先判断当前栈是否为空,空那么标记当前位置,如果不空,
弹出栈顶并解除标记,开个字符数组来存标记最后输出即可
#include<iostream> #include<string.h> #include<stack> using namespace std; char a[110],b[110]; int n; int main() { while(cin >> a) { stack<int>ss; //注意如果栈开在外面每次都要把全部栈中元素弹出 memset(b,' ',sizeof(b)); n=strlen(a); cout << a << endl; for(int i=0;i<n;i++) { if(a[i]=='(') { ss.push(i); b[i]='$'; } if(a[i]==')') { if(!ss.empty()) { int t=ss.top(); b[t]=' '; ss.pop(); } else { b[i]='?'; } } } for(int i=0;i<n;i++) { cout << b[i] ; } cout << endl; } return 0; }

浙公网安备 33010602011771号