一、集合的划分(信息学奥赛一本通-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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
二、数的计数(信息学奥赛一本通-T1316):
这道题暴力的递归会超时,所以用记忆化递归(类似记忆化搜索)来做...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
三、逆波兰表达式(信息学奥赛一本通-T1198):
这道题虽然说没有优先级,但是感觉隐隐约约还是有的:离数字越近的运算优先级越高...然后这个题中有一个函数:atof,用来把一个string类型的数变换成float类型...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
四、全排列(信息学奥赛一本通-T1199):
这道题用到了回溯的一个思想:
我们如果长度达到了len,那么就输出。如果还没有,那么就一位一位枚举,如果它并没有在当前的序列中出现过,那么就把它加进来,然后找下一个。注意回溯...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
五、分解因数(信息学奥赛一本通-T1200):
我们用a数组存储现在已经得到的这个元素的因数,然后看看除它以外还有多少因数,最后如果等于,则tot++,但是不明白为什么a[0]初始化为2...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
六、斐波那契数列(信息学奥赛一本通-T1201):
这道题可以直接用递推做,实在太水...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
七、Pell数列(信息学奥赛一本通-T1202):
这道题跟T6太像了,注意不断的模就可以了...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
八、扩号匹配问题(信息学奥赛一本通-T1203):
这道题并没有看出有什么递归,只是用了两个栈进行模拟...竟然水过了!!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
九、爬楼梯(信息学奥赛一本通-T1204):
这道题只要手模一下即可得出规律:
f[1] = 1,f[2] = 2,f[x] = f[x - 1] + f[x - 2] (x > 2)
然后可以先全部预处理一下,也可以每一个都进行递归..
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十、汉诺塔问题(信息学奥赛一本通-T1205):
这道题比较晕,对汉诺塔的原理不是很清楚:
为了解决这个问题,不妨假设已经知道怎样移动N-1个圆环了。现在,为了把起点盘上的圆环移动到目标盘,需要做如下操作:
1、把N-1个圆环从起点盘移动到(当前)没有任何圆环的过渡盘;
2、把最后一个圆环从起点盘移动到目标盘;
3、把N-1个圆环从过渡盘移动到目标盘(模仿1和2的操作方法来实现)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十一、放苹果(信息学奥赛一本通-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的时候,都只有一种方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十二、求最大公约数问题(信息学奥赛一本通-T1207):
没什么好解释的,gcd的模板,也很好理解...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十三、2的幂次方表示(信息学奥赛一本通-T1208):
把一个数想成二进制,那么像十进制一样,取最后一位则为%2,详见代码...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十四、分数求和(信息学奥赛一本通-T1209):
这道题思路很简单,暴力地相加,先不约分,将所有分母相乘作为分母,然后看每一个分数的分母比原来扩大了多少倍,分子跟着扩大多少倍。然后找它们的gcd,最后约分,注意分母为1即可...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十五、因子分解(信息学奥赛一本通-T1210):
这道题很简单,没有想象中那样复杂,并不需要欧拉筛,因为是从小到大找因数,所以找到的因数都是质数,反证法可以证明...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
十六、判断元素是否存在(信息学奥赛一本通-T1211):
一开始自己的思路和正解正好相反,自己是将所有的集合中的可能进行一个初始化,可是发现并没有边界,所以这道题的思路是逆着来的,看x按照题中的规律往后退,看看能否推到k,注意第十行,不能没有...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }