题目上添加了超链接,大家点一下题目就会自动跳转到Poj原题界面~~ 冲鸭冲鸭ヾ(◍°∇°◍)ノ゙。
前言:
枚举策略是大家即使没有接触过计算机,面对问题时也可以很自然想到的直男方法;
递归策略则可以算作计算机专业同学相较于其他专业同学的专属思维方式了,所以初见时理解起来有障碍很正常。
但这个宝贝是未来我们离不开手的兵器~
2.2.1 Function Run Fun (1579)
题意:计算题目里的递归函数。
小笔记:递归由于存在重复调用、且每一次调用都单独计算,很容易造成大量计算,这里为避免重复计算,预先将w(0,0,0)~w(20,20,20)之间的函数值用三维数组v保存起来即可,数组初始化为0。这个思想很重要。
#include <cstdio> #include <cstring> using namespace std; int v[21][21][21]; int w(int a, int b, int c) { if (a <= 0 || b <= 0 || c <= 0) return 1; if (a > 20 || b > 20 || c > 20) return w(20, 20, 20); if (!v[a][b][c]) { if (a < b && b < c) v[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c); else v[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1); } return v[a][b][c]; } int main() { int a, b, c; while (scanf("%d%d%d", &a, &b, &c)) { memset(v, 0, sizeof(v)); if (a == -1 && b == -1 && c == -1) break; printf("w(%d, %d, %d) = %d ", a, b, c, w(a, b, c)); } return 0; }
2.2.2 Tree Recovery (2255)
题意:给定二叉树的先序遍历序列S1和中序遍历序列S2,求其后序遍历序列。
小笔记:这题不涉及数据结构,大家只要在导论课里知道什么是二叉树以及二叉树如何遍历的就可以理解这道题。不要害怕。strchr函数功能为在一个串中查找给定字符的第一个匹配之处,包含在<cstring>头文件下。
#include <cstdio> #include <cstring> using namespace std; void postorder(char *left, char *right, int n) { char root = left[0]; if (n > 0) { int m = strchr(right, root) - right; //后序遍历左子树 postorder(left + 1, right, m); //后序遍历右子树 postorder(left + m + 1, right + m + 1, n - m - 1); printf("%c", root); } } int main() { char s1[30], s2[30]; while (~scanf("%s%s", s1, s2)) { postorder(s1, s2, strlen(s1)); printf(" "); } return 0; }
2.2.3 Fractal (2083)
题意:打印题目中的分形图形DNA链。
小笔记:在本题中将图案分为左上、右上、中、左下、右下5个部分,找到这几部分在图案中的左上角坐标(i,j)进行递归。不知道多少同学这种题是找规律做的...确实可以,没问题。
#include <cstdio> #include <cstring> using namespace std; char x[730][730]; int p[9] = {0, 0, 1, 3, 9, 27, 81, 243, 729}; //用于确定坐标位置 void print(int n, int i, int j) { if (n == 1) { x[i][j] = 'X'; return; } print(n - 1, i, j); print(n - 1, i + p[n] * 2, j); print(n - 1, i + p[n], j + p[n]); print(n - 1, i, j + p[n] * 2); print(n - 1, i + p[n] * 2, j + p[n] * 2); } int main() { memset(x, ' ', sizeof(x)); print(7, 0, 0); int n; while (scanf("%d", &n) && ~n) { for (int i = 0; i < p[n + 1]; i++) { x[i][p[n + 1]] = ' '; puts(x[i]); x[i][p[n + 1]] = ' '; } printf("- "); } return 0; }
2.2.4 放苹果 (1664)
题意:把m个同样的苹果放在n个同样的盘子里,允许有的盘子空着不放,输出不同的分法的总数。
小笔记:整数拆分问题,实际问题中经典的递归问题。很经典,抽象出递归策略还是有难度的。要好好吸收这道题的思想。
#include <cstdio> using namespace std; int f(int m, int n) { if (m < 0 || n == 0) //特殊情况1(出口) return 0; if (m == 1 || n == 1) //特殊情况2(出口) return 1; return f(m, n - 1) + f(m - n, n); /* n中至少有一个为空,这种情况总数为f(m,n-1); 没有空盘子,这时每个盘子至少有一个苹果,这种情况总数f(m-n,n)。 */ } int main() { int t; scanf("%d", &t); while (t--) { int m, n; scanf("%d%d", &m, &n); printf("%d ", f(m, n)); } return 0; }
2.2.5 Orders (1731)
题意:给出字母序列,将这个序列中字母组成的所有可能排列(置换)按字典序输出。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; char a[200]; //输入序列 char b[200]; //输出序列 bool v[200]; //记录序列中字母是否被使用 void perm(unsigned int k) { char c = ' '; //记录本层递归使用的字母 if (k == strlen(a)) { b[k] = ' '; puts(b); } for (int i = 0; a[i]; i++) { if (!v[i] && a[i] != c) { b[k] = a[i]; v[i] = true; c = a[i]; perm(k + 1); v[i] = false; } } } int main() { scanf("%s", a); sort(a, a + strlen(a));//排出字典序 perm(0); return 0; }
小笔记:对于全排列问题,还可以使用C++ STL中的next_permutation函数直接实现。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int main() { char s[200]; scanf("%s", s); sort(s, s + strlen(s)); puts(s); while (next_permutation(s, s + strlen(s))) puts(s); return 0; }