递归
1.基本思想:一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,通常把一个大型的复杂的问题转化为一个与原问题相似的规模较小的问题来求解。
2.算法特点:思路简洁但是运行效率比较低。
3.比较经典的有汉诺塔问题和八皇后问题,这两个问题都能在网上找到详细的讲解(推荐一下郭炜老师讲的八皇后问题,讲得真是特别好)。
4.课后测验:
a.全排列
思路:有点类似深度优先遍历
用一个flag数组标记字符数组中的字符是否被填写到结果数组,如果没有填写,则将其加入并且递归填写结果数组的下一个位置,因为要输出所有全排列的结果,所以递归结束后要将flag数组置为未填写,以便进行下一轮的递归。
代码如下:
# include<iostream>
# include<cstring>
using namespace std;
int len,flag[10];//flag用于标记同下标的字符是否被访问过
char c[10];
void f(char a[],int k)
{
if (k == len)
{
for (int i = 0; i < k; i++)
cout << a[i];
cout << endl;
}
for (int i = 0; i < len; i++)
{
if (!flag[i])
{
flag[i] = 1;
a[k] = c[i];
f(a, k + 1);
flag[i] = 0;
}
}
}
int main()
{
cin >> c;
len = strlen(c);
char a[10];
f(a,0);
return 0;
}
b.2的次幂方表示
思路:用string类做这题比较好,直接看看代码吧,应该比较好理解。
# include<iostream>
# include<string>
using namespace std;
void f(string &s, int n)
{
if (n == 0)
return;
if (n == 1)
s += "2(0)";
else if (n == 2)
s += "2";
else
{
int v = 1, i = -1;
while (v <= n)
{
v = 1;
i++;
v = v << i;
}
v = v >> 1;
i--;
if (i == 1)
s += "2";
else
{
s += "2(";
f(s, i);
s += ")";
}
if (n - v != 0)
{
s += "+";
f(s, n - v);
}
}
}
int main()
{
string s;
int n;
cin >> n;
f(s, n);
cout << s;
return 0;
}
注意到24行加了一个判断是否i为1,如果不加这个判断,结果就会出现2(2(0))这样的现象。
c:Boolean Expressions
思路:和四则运算表达式求值同样的思路。表达式由因子和因子的&&或||组成,因子由“V”、“F”或“(”+“表达式”+“)”组成,或者由他们的!组成,这就出现了递归定义,照着定义套代码就可以了,注意一些细节的处理。
# include<iostream>
using namespace std;
bool factor();
bool expression()
{
bool result = factor();
while (1)
{
char c = cin.peek();
if (c == ' ')
cin.get();
else if (c == '|' || c == '&')
{
cin.get();
bool value = factor();
if (c == '|')
{
result = result || value;
}
else
{
result = result && value;
}
}
else
break;
}
return result;
}
bool factor()
{
bool result = true;
while (1)
{
char c = cin.peek();
if (c == ' ')
{
cin.get();
}
else if (c == 'V')
{
result = true;
cin.get();
}
else if (c == 'F')
{
result = false;
cin.get();
}
else if (c == '!')
{
cin.get();
bool value = factor();
result = !value;
}
else if (c == '(')
{
cin.get();
result = expression();
cin.get();
}
else
break;
}
return result;
}
int main()
{
int count = 1;
while (cin.peek() == '(' || cin.peek() == 'V' || cin.peek() == 'F' || cin.peek() == '&' || cin.peek() == '|' || cin.peek() == '!')
{
bool result = expression();
if (result)
{
cout << "Expression " << count++ << ": " << "V"<<endl;
}
else
{
cout << "Expression " << count++ << ": " << "F"<<endl;
}
cin.get();
}
return 0;
}
值得注意的有cin.get()和cin.peek()的用法,我打算在其他文章中详细的说一说。
d.简单的整数划分问题
思路:这题就是要找到递推公式,找到公式后就容易许多了。这里定义两个变量:n,m(n表示要划分的数字,m表示划分中的最大数字,最大数字不一定存在划分中,小于等于m都可以)。
分四种情况讨论。
第一种:n或m等于1,n等于1时,只能划分为1,m=1时只能划分为n个1的和的形式,都只有一种划分方法;
第二种:n等于m时,一种划分是只有一个m这种情况,另一种就是最大数字为m-1的划分的情况;
第三种:n小于m,此时只有最大值为n时的划分,回到了第二种情况,因为不能出现负数的划分;
第四种:n大于m,一种划分情况是划分数字为n-m并且最大数字为m的情况,另一种是划分数字为n并且最大数字为m-1的情况(即至少包含一个m和不包含m的情况)。
得到递推公式:
1;(n=1或m=1)
1+f(n,m-1);(n=m)
f(n,n);(n<m)
f(n-m,m)+f(n,m-1);(n>m)
代码如下:
# include<iostream>
using namespace std;
int f(int n, int m)
{
if (n == 1 || m == 1)
return 1;
else if (n == m)
{
return (1 + f(n,m - 1));
}
else if (n < m)
{
return f(n, n);
}
else
{
return f(n - m, m) + f(n, m - 1);
}
}
int main()
{
int n;
while(cin >> n)
cout << f(n,n)<<endl;
return 0;
}