递归就是自己调用自己
基本递归
题目:小猴吃桃子
一只小猴买了若干个桃子。第一天他刚好吃了这些桃子的一半,又贪嘴多吃了一个;接下来的每一天它都会吃剩余的桃子的一半外加一个。第n(n≤20)天早上起来一看,只剩下 1 个桃子了。请问小猴买了几个桃子?
#include <iostream>
using namespace std;
int n, s;
void f (int t) {
t--;
if (t == 0) {
return;
}
//现在的s是上一天剩余的桃子除2减1
s++; //先加一
s *= 2; //再乘2
//先s++再乘2
f(t); //递归部分,自己调用自己
return;
}
int main () {
cin >> n;
s = 1; //最后一天剩1个
f(n); //倒推,从n开始
cout << s;
return 0;
}
【递归】二分算法(二分查找)
顾名思义,二分就是把一个数组折半分开
贴简单二分代码:
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, x;
int a[500009];
bool f (int l, int r, int t) {
if (l > r) {
//没有找到
return false;
}
int mid = (l + r) / 2;
if (t < a[mid]) {
//查找的数的大小在a[mid]左边(因为有序)
//查找范围缩小到最左边至中间
return f(l, mid - 1, t);
}
if (t > a[mid]) {
//查找的数的大小在a[mid]右边(因为有序)
//查找范围缩小到中间至最右边
return f(mid + 1, r, t);
}
return true; //剩下的情况为 t == a[mid], 查找到了
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
//先排序(做二分算法时数组必须有序)
sort (a + 1, a + n + 1);
cin >> m; //m组查找
for (int i = 1; i <= m; i++) {
cin >> x;
cout << (f(1, n, x)?"YES":"NO") << endl; //是否查找到
}
return 0;
}
来测试几个样例~
例子:
数组大小:8
数组:
index:1,2,3,4,5,6,7,8
[5, 1, 3, 8, 11, 14, 7, 10]
排完序:
index:1,2,3,4, 5, 6, 7, 8
[1,3,5,7,8,10,11,14]
如果查找数为3:
mid = (1 + 8) / 2 = 4
a[4] = 7
3 < 7
return f (1, 4 - 1, 3);
↓
mid = (1 + 3) / 2 = 2;
a[2] = 3;
return true;
如果查找数为9
mid = (1 + 8) / 2 = 4;
a[4] = 7;
9 > 7
return f (4 + 1, 8, 9);
↓
mid = (5 + 8) / 2 = 6;
a[6] = 10;
9 < 10;
return (5, 6 - 1, 9);
↓
mid = (5 + 5) / 2 = 5;
a[5] = 8;
9 > 8;
return f (5 + 1, 5, 9);
↓
l = 6; r = 5
l > r
return false;
的确
【递归】快速幂
从本质上看,快速幂和二分有点(一点点)像
#include <iostream>
using namespace std;
int n, m;
long long s;
//int不行,要用long long
long long pw (int num, int t) {
if (t == 0) {
return 1; //不要忘了n的零次方永远是1
}
if (t == 1) {
return num;
}
// 也是除2,与二分相似
// ↑↑ ↑↑
long long x = pw(num, t / 2);
if (t % 2 == 0) {
// 例如3^4中4是偶数,拆成 3 ^ 2 * 3 ^ 2
return x * x;
}
// 例如3^5中5是不是偶数,拆成 3 * 3 ^ 2 * 3 ^ 2
return num * x * x;
}
int main () {
cin >> n >> m;
//求n的m次方
s = pw(n, m);
// m次方的值增长得非常快,2^128 = 340282366920938463463374607431768211456
// long long也存不下呢。。。
cout << s;
return 0;
}
取余:
计算某些n的m次方时,可能会
所以
可以在计算过程中对s取余
long long pw (long long num, long long t) {
if (t == 0) {
return 1;
}
if (t == 1) {
return num;
}
long long x = pw(num, t / 2);
if (t % 2 == 0) {
return (x * x) % 9223372036854775807;
}
return (num * x * x) % 9223372036854775807;
//9223372036854775807为long long最大值
}
当然,也有绝妙之法的
题目:求结果
说明:
给你如下公式,请你求出下面公式的结果
答案太大了,请输出对2^64取模后的结果
输入格式:
第一行一个n,
后面n行,每行一对a[i], b[i];。
0 < n, a[i], b[i] ≤ 10^5
输出格式:
输出答案对2^64取模后的结果。
2^64连long long范围都超了 |
思路:
#include <iostream>
using namespace std;
long long n, a, b;
//使用unsigned long long
//↓↓↓ ↓↓↓
unsigned long long s;
long long pw (long long num, long long t) {
if (t == 1) {
return num;
}
long long x = pw(num, t / 2);
// 竟然不用取余!!!
if (t % 2 == 0) {
return x * x;
}
return num * x * x;
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a >> b;
s += pw(a, b);
}
cout << s;
return 0;
}
让他溢出就好
原理:
假设二进制
1,11111000,00001100,00000000,1000000,00110100,00011000,01011100,00010000
它已超出long long范围
会将其自动改成:
11111000,00001100,00000000,1000000,00110100,00011000,01011100,00010000
而省略掉的最前面的1,就代表2^64。
【递归】归并排序
说起来,归并排序的代码很
长
#include <iostream>
using namespace std;
int n;
int a[100009];
int t[100009];
void mergeSort (int l, int mid, int r) {
int i, j, k;
i = l; k = l;
j = mid + 1;
while (i <= mid && j <= r) {
if (a[i] <= a[j]) {
t[k++] = a[i++];
}
else {
t[k++] = a[j++];
}
}
// 左边有剩余 ↓
while (i <= mid) {
t[k++] = a[i++];
}
// 右边有剩余 ↓
while (j <= r) {
t[k++] = a[j++];
}
// 部分输出
for (i = l; i <= r; i++) {
a[i] = t[i];
cout << t[i] << " ";
}
cout << endl;
}
void f (int l, int r) {
if (l == r) {
return;
}
int mid = (l + r) / 2;
//分成两半 ↓
f(l, mid); //左
f(mid + 1, r); //右
mergeSort(l, mid, r);
return;
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
f(1, n);
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
return 0;
}
细节:打架
STEP1
STEP2
STEP3
STEP4
大功告成!!!
输出结果: