递归练习

一、集合的划分(信息学奥赛一本通-T1315):

关于集合自己并不是理解的很透彻:
关于a[i]的处理有两种:

1.a[i]是k个子集中的一个,于是便把a[1],a[2]......a[i - 1]划分为了k - 1个子集,这种情况共有s(n - 1, k - 1)个

2.a[i]不是k个子集中的一个,那么它肯定与其他元素构成一个子集,这种情况共有k * s(n - 1, k)个

根据乘法、加法原理,得到递归式:s(n, k) = s(n - 1, k - 1) + k * s(n - 1, k),然后注意一些边界条件,注意s很大,要开long long

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 inline long long s(int n, int k){
 5     if(k == 1 || k == n) return 1;//边界 
 6     if(k == 0 || n < k) return 0;//边界 
 7     return s(n - 1, k - 1) + k * s(n - 1, k);//递归式 
 8 }
 9 
10 int main(){
11     int n, k;
12     scanf("%d%d", &n, &k);
13     printf("%lld", s(n, k));
14     return 0;
15 }
1315

二、数的计数(信息学奥赛一本通-T1316):

这道题暴力的递归会超时,所以用记忆化递归(类似记忆化搜索)来做...

 1 #include<cstdio>
 2 #include<cstring>
 3 
 4 using namespace std;
 5 
 6 int h[1005];
 7 
 8 inline void dfs(int x){
 9     if(h[x] != -1) return;//如果不等于-1,说明已经处理过了
10     h[x] = 1;//它本身一种情况
11     for(int i = 1; i <= x / 2; i++){
12         dfs(i);//按题目要求
13         h[x] += h[i];//i的情况肯定是x中的情况,因为i比x小
14     }
15 }
16 
17 int main(){
18     int n;
19     memset(h, -1, sizeof(h));//初始化
20     scanf("%d", &n);
21     dfs(n);
22     printf("%d", h[n]);
23     return 0;
24 }
1316

三、逆波兰表达式(信息学奥赛一本通-T1198):

这道题虽然说没有优先级,但是感觉隐隐约约还是有的:离数字越近的运算优先级越高...然后这个题中有一个函数:atof,用来把一个string类型的数变换成float类型...

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

char s[1000005];

inline double dfs(){
    scanf("%s", s);
    if(s[0] == '+') return dfs() + dfs();
    else if(s[0] == '-') return dfs() - dfs();
    else if(s[0] == '*') return dfs() * dfs();
    else if(s[0] == '/') return dfs() / dfs();
    else return atof(s);//函数 
}

int main(){
    printf("%f", dfs());
    return 0;
}
1198

四、全排列(信息学奥赛一本通-T1199):

这道题用到了回溯的一个思想:

我们如果长度达到了len,那么就输出。如果还没有,那么就一位一位枚举,如果它并没有在当前的序列中出现过,那么就把它加进来,然后找下一个。注意回溯...

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 
 7 char s[10], now[10];
 8 int vis[10], len;
 9 
10 inline void dfs(int l){
11     if(l == len){
12         for(int i = 0; i < l; i++)
13             printf("%c", now[i]);
14         printf("
");
15     }
16     else{
17         for(int i = 0; i < len; i++){
18             if(!vis[i]){
19                 now[l] = s[i];
20                 vis[i] = 1;
21                 dfs(l + 1);
22                 vis[i] = 0;//回溯 
23             }
24         }
25     }
26 }
27 
28 int main(){
29     scanf("%s", s);
30     len = strlen(s);
31     dfs(0);
32     return 0;
33 }
1199

五、分解因数(信息学奥赛一本通-T1200):

我们用a数组存储现在已经得到的这个元素的因数,然后看看除它以外还有多少因数,最后如果等于,则tot++,但是不明白为什么a[0]初始化为2...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 int m, a[40004], tot;
 7 
 8 inline void dfs(int x, int cur){
 9     int ans = 1;
10     for(int i = 1; i <= cur - 1; i++)
11         ans *= a[i];
12     if(ans == m) {tot++; return;}//正好 
13     if(ans > m) return;
14     for(int i = a[cur - 1]; i <= x; i++){
15         if(x % i == 0){ 
16             x /= i;
17             a[cur] = i;
18             dfs(x, cur + 1);
19             x *= i;//回溯 
20         }
21     }
22 }
23 
24 int main(){
25     int n;
26     scanf("%d", &n);
27     for(int i = 1; i <= n; i++){
28         scanf("%d", &m);
29         tot = 0;
30         a[0] = 2;
31         dfs(m, 1);
32         printf("%d
", tot);
33     }
34     return 0;
35 }
1200

六、斐波那契数列(信息学奥赛一本通-T1201):

这道题可以直接用递推做,实在太水...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 int f[25], a;
 7 
 8 inline void work(){
 9     f[1] = 1;
10     f[2] = 1;
11     for(int i = 3; i <= 20; i++){
12         f[i] = f[i - 1] + f[i - 2];
13     }
14     return;
15 }
16 
17 int main(){
18     int n;
19     scanf("%d", &n);
20     work();
21     for(int i = 1; i <= n; i++){
22         scanf("%d", &a);
23         printf("%d
", f[a]);
24     }
25     return 0;
26 }
1201

七、Pell数列(信息学奥赛一本通-T1202):

这道题跟T6太像了,注意不断的模就可以了...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 int a[1000005];
 7 
 8 inline void work(){
 9     for(int i = 3; i <= 1000005; i++)
10         a[i] = 2 * a[i - 1] % 32767 + a[i - 2] % 32767;
11     return;
12 }
13 
14 int main(){
15     int n;
16     scanf("%d", &n);
17     a[1] = 1;
18     a[2] = 2;
19     work();
20     for(int i = 1; i <= n; i++){
21         int x;
22         scanf("%d", &x);
23         printf("%d
", a[x] % 32767);
24     }
25     return 0;
26 }
1202

 八、扩号匹配问题(信息学奥赛一本通-T1203):

这道题并没有看出有什么递归,只是用了两个栈进行模拟...竟然水过了!!

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<stack>
 5 
 6 using namespace std;
 7 
 8 stack<int> st1;
 9 stack<int> st2;
10 
11 char s[105];
12 int vis[105], l;
13 
14 int main(){
15     while(scanf("%s", s) == 1){
16         memset(vis, 0, sizeof(vis));
17         l = strlen(s);
18         for(int i = 0; i < l; i++){
19             if(s[i] != '(' && s[i] != '[' && s[i] != ']' && s[i] != ')') continue;
20             if(s[i] == '(' || s[i] == '['){
21                 if(s[i] == '(') st1.push(i);
22                 else st2.push(i);
23             }
24             else if(s[i] == ')' || s[i] == ']'){
25                 if(s[i] == ')' && !st1.empty()) {st1.pop(); continue;}
26                 if(st1.empty()) vis[i] = 2;
27                 if(s[i] == ']' && !st2.empty()) {st2.pop(); continue;}
28                 if(st2.empty()) vis[i] = 2;
29             }
30             else{
31                 if(s[i] == '(' || s[i] == '[') vis[i] = 1;
32                 else vis[i] = 2;
33             }
34         }
35         while(!st1.empty()){
36             vis[st1.top()] = 1;
37             st1.pop();
38         }
39         while(!st2.empty()){
40             vis[st2.top()] = 1;
41             st2.pop();
42         }
43         printf("%s
", s);
44         for(int i = 0; i < l; i++){
45             if(vis[i] == 1) printf("$");
46             else if(vis[i] == 2) printf("?");
47             else printf(" ");
48         }
49         printf("
");
50     }
51     return 0;
52 }
53     
1203

九、爬楼梯(信息学奥赛一本通-T1204):

这道题只要手模一下即可得出规律:

f[1] = 1,f[2] = 2,f[x] = f[x - 1] + f[x - 2] (x > 2)

然后可以先全部预处理一下,也可以每一个都进行递归..

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 
 7 inline int dfs(int x){
 8     if(x == 1) return 1;
 9     else if(x == 2) return 2;
10     else return dfs(x - 1) + dfs(x - 2);
11 }
12 
13 int main(){
14     int n;
15     while(scanf("%d", &n) == 1){
16         printf("%d
", dfs(n));
17     }
18     return 0;
19 }
1204

十、汉诺塔问题(信息学奥赛一本通-T1205):

这道题比较晕,对汉诺塔的原理不是很清楚:

为了解决这个问题,不妨假设已经知道怎样移动N-1个圆环了。现在,为了把起点盘上的圆环移动到目标盘,需要做如下操作:

1、把N-1个圆环从起点盘移动到(当前)没有任何圆环的过渡盘;

2、把最后一个圆环从起点盘移动到目标盘;

3、把N-1个圆环从过渡盘移动到目标盘(模仿1和2的操作方法来实现)。

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 inline void dfs(int n, char st, char gd, char end){
 7     if(n == 1){
 8         printf("%c->%d->%c
", st, n, end);
 9         return;
10     }
11     dfs(n - 1, st, end, gd);
12     printf("%c->%d->%c
", st, n, end);
13     dfs(n - 1, gd, st, end);
14 }
15 
16 int main(){
17     int n;
18     char a, b, c;
19     cin >> n >> a >> b >> c;
20     dfs(n, a, c, b);
21     return 0;
22 }
1205

十一、放苹果(信息学奥赛一本通-T1206):

f(m,n)表示m个苹果放到n个盘子中的方法数
当n>m时,必然有盘子是空的,因为最多也就用到m个盘子,因此f(m,n)=f(m,m);
当n<=m时,有两种情况:
有盘子为空时:
f(m,n)=f(m,n-1)因为至少有一个为空,那去掉一个完全不影响已有的方法数(反正这个空盘子不会放苹果)
当盘子不空的时候,全部n个盘子都有装苹果,那所有的都可以拿掉一个苹果,也就是
f(m,n)=f(m-n,n) 方法数是一样的,只不过所有盘子都用上的时候每个盘子装的数量可能不一样
而n<=m的时候就是有空和没空两种情况的和:f(n,m)=f(m,n-1)+f(m-n,n) 
两者递归时,n和m都会逐渐减小 ,出口为n==1||m==0的时候,都只有一种方法

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 inline int dfs(int m, int n){
 7     if(m == 0 || n == 1) return 1;
 8     if(m < n) return dfs(m, m);
 9     else return dfs(m, n - 1) + dfs(m - n, n);
10 }
11 
12 int main(){
13     int t, n, m;
14     scanf("%d", &t);
15     while(t--){
16         scanf("%d%d", &m, &n);
17         printf("%d
", dfs(m, n));
18     }
19     return 0;
20 }
1206

十二、求最大公约数问题(信息学奥赛一本通-T1207):

没什么好解释的,gcd的模板,也很好理解...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 inline int gcd(int a, int b){
 7     return b == 0 ? a : gcd(b, a % b);
 8 }
 9 
10 int main(){
11     int a, b;
12     scanf("%d%d", &a, &b);
13     printf("%d", gcd(a, b));
14     return 0;
15 }
1207

十三、2的幂次方表示(信息学奥赛一本通-T1208):

把一个数想成二进制,那么像十进制一样,取最后一位则为%2,详见代码...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 inline void dfs(int n, int cur){
 7     //n为被分解数,cur为二进制位数,n % 2为位数上的数值 
 8     if(n == 0) return;
 9     dfs(n / 2, cur + 1);
10     if(n % 2){
11         if(n / 2) printf("+");//如果n不为0且n % 2不为0证明这不是第一个2的幂次方,输出+
12         if(cur == 1) printf("2");
13         else{
14             printf("2(");
15             if(cur == 0) printf("0");
16             else dfs(cur, 0);
17             printf(")");
18         }
19     }
20 }
21 
22 int main(){
23     int n;
24     scanf("%d", &n);
25     dfs(n, 0);
26     return 0;
27 }
1208

十四、分数求和(信息学奥赛一本通-T1209):

这道题思路很简单,暴力地相加,先不约分,将所有分母相乘作为分母,然后看每一个分数的分母比原来扩大了多少倍,分子跟着扩大多少倍。然后找它们的gcd,最后约分,注意分母为1即可...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 bool fir;
 7 
 8 int p[15], q[15];
 9 int ans1, ans2 = 1;
10 char c;
11 
12 inline int gcd(int a, int b){
13     return b == 0 ? a : gcd(b, a % b);
14 }
15 
16 int main(){
17     int n;
18     scanf("%d", &n);
19     for(int i = 1; i <= n; i++){
20         scanf("%d%c%d", &p[i], &c, &q[i]);
21     }
22     for(int i = 1; i <= n; i++) ans2 *= q[i];
23     for(int i = 1; i <= n; i++){
24         ans1 += (ans2 / q[i]) * p[i];
25     }
26     int t = gcd(ans1, ans2);
27     if(ans2 / t == 1) printf("%d", ans1);
28     else printf("%d/%d", ans1 / t, ans2 / t);
29     return 0;
30 }
1209

十五、因子分解(信息学奥赛一本通-T1210):

这道题很简单,没有想象中那样复杂,并不需要欧拉筛,因为是从小到大找因数,所以找到的因数都是质数,反证法可以证明...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 int n, cnt;
 7 int tot[105];
 8 bool fir;
 9 
10 inline void dfs(int n, int l){
11     if(n == 0 || l > n) return;
12     while (n % l == 0){
13         tot[l] ++;
14         n /= l;
15     }
16     dfs(n, l + 1);
17 }
18 
19 inline void work(){
20     for(int i = 2; i <= n; i++){
21         if(tot[i] == 0) continue;
22         if(!fir){
23             if(tot[i] == 1) printf("%d", i);
24             else printf("%d^%d", i, tot[i]);
25             fir = 1;
26         }
27         else{
28             if(tot[i] == 1) printf("*%d", i);
29             else printf("*%d^%d", i, tot[i]);
30         }
31     }
32 }
33 
34 int main(){
35     scanf("%d", &n);
36     dfs(n, 2);
37     work();
38     return 0;
39 }
1210

十六、判断元素是否存在(信息学奥赛一本通-T1211):

一开始自己的思路和正解正好相反,自己是将所有的集合中的可能进行一个初始化,可是发现并没有边界,所以这道题的思路是逆着来的,看x按照题中的规律往后退,看看能否推到k,注意第十行,不能没有...

 1 #include<cstdio>
 2 #include<iostream>
 3 
 4 using namespace std;
 5 
 6 int k;
 7 
 8 inline int dfs(int x){
 9     if(x == k) return 1;
10     if((x - 1) % 2 == 0 && (x - 1) % 3 == 0) return (dfs((x - 1) / 2) || dfs((x - 1) / 3));
11     if((x - 1) % 2 == 0) return dfs((x - 1) / 2);
12     if((x - 1) % 3 == 0) return dfs((x - 1) / 3);
13     return 0;
14 }
15 
16 int main(){
17     int x;
18     scanf("%d,%d", &k, &x);
19     if(dfs(x)) printf("YES");
20     else printf("NO");
21     return 0;
22 }
1211
原文地址:https://www.cnblogs.com/New-ljx/p/11299369.html