《算法进阶指南》- 0.2递推与递归
2022/1/31 1:05:57
本文主要是介绍《算法进阶指南》- 0.2递推与递归,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
总结:递归、递推提高效率,其他题还好一些,此章解决了我之前对汉诺塔的疑惑,提升了对二进制表示状态的理解,但最后一题分形之城还是有点模糊,在后续学习中常回头。
递归
>递归实现指数型枚举
dfs写法是对于每一位,走两条路:选或者不选,当对n个路口都做出选择时,输出结果。
#include<iostream> using namespace std; int n; void dfs(int u,int state) { if(u == n) { for(int i = 0;i < n;i++) if(state>>i & 1) cout<<i + 1<<' '; puts(""); return ; } //不选 dfs(u + 1,state); //选 dfs(u + 1,state | 1 << u); } int main() { cin>>n; dfs(0,0); return 0; }
暴力写法则是枚举所有状态,输出方案。
#include<iostream> using namespace std; int main() { int n; cin>>n; for(int i = 0;i < (1 << n) ;i++) { for(int j = 0;j < 15;j++) { if(i >> j & 1) cout<<j + 1<<' '; } puts(""); } return 0; }
>递归实现组合型枚举
跟指数型枚举类似,多了一维表示当前选了几个的参数,对于每一个位,选或者不选两条分支,当前已经选了m个时,输出方案。
#include<iostream> using namespace std; int n,m; void dfs(int u,int cnt,int state) { if(cnt == m) { for(int i = 0;i < n;i++) if(state>>i & 1) cout<<i + 1<<' '; puts(""); return ; } if(u == n) return ; //选 dfs(u + 1,cnt + 1,state | 1 << u); //不选 dfs(u + 1,cnt,state); } int main() { cin>>n>>m; dfs(0,0,0); return 0; }
>递归实现排列型枚举
典型dfs,依次填数,之前没用过的话才能用,依次遍历没用过的,放满了就输出。注意要恢复状态(回溯)
#include<bits/stdc++.h> using namespace std ; vector<int> path; int n; void dfs(int u,int state) { if(u == n) { for(int i = 0;i < n;i++) cout<<path[i]<<' '; puts(""); return ; } for(int i = 0;i < n;i++) { if(!(state>>i & 1)) { path.push_back(i + 1); dfs(u + 1,state | (1 << i)); path.pop_back(); } } } int main() { cin>>n; dfs(0,0); return 0; }
>约数之和
等比数列的和可以通过分治,配合快速幂在\(log(c)\)的时间内求出
#include<iostream> using namespace std; const int mod = 9901; int qmi(int a,int b)//快速幂 { a %= mod; int res = 1; while(b) { if(b&1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } int sum(int p,int c)//分治求sum { if(c == 0) return 1; if(c & 1) return (1 + qmi(p,(c + 1 )/ 2)) % mod * sum(p,(c - 1) / 2) % mod; return ((1 + qmi(p,c / 2)) % mod * sum(p,(c / 2) - 1) + qmi(p,c)) % mod; } int main() { int a,b; cin>>a>>b; int res = 1; for(int i = 2;i <= a;i++) { int s = 0; while(a % i == 0) { s++; a /= i; } if(s) res = res * sum(i,s * b) % mod; } if(!a) res = 0; cout<<res<<endl; return 0; }
此题有点模糊
>分形之城
根据给的等级,编号找到其对应的坐标,然后根据题目中的等级一进行坐标变换。优质题解
#include<algorithm> #include<cmath> #include<iostream> using namespace std; typedef long long LL; typedef pair<LL,LL> PLL; PLL cacl(LL n,LL m) { /* n:等级 m:编号 */ if(n == 0) return {0,0}; LL len = 1ll << (n - 1);//象限边长 LL cnt = 1ll << (2 * n - 2);//等级n - 1中容量 PLL pos = cacl(n-1,m % cnt); LL x = pos.first,y = pos.second; int z = m / cnt;//处于哪个象限 if(z == 0) return {-y - len,-x + len}; else if(z == 1) return {x + len,y + len}; else if(z == 2) return {x + len,y - len}; return {y - len,x - len}; } int main() { int T; cin>>T; while(T--) { LL N,A,B; cin>>N>>A>>B; auto ac = cacl(N,A-1); auto bc = cacl(N,B-1); double x = ac.first - bc.first, y = ac.second - bc.second; printf("%.0lf\n",sqrt(x * x + y * y) * 5); } return 0; }
递推
>费解的开关
枚举第一行按哪个的所有状态(\(2^5\)种),这样按后已经确定第一行。例如:确定状态后,第一行是0,只能通过按第二行来改变成1,这样依次推下去,如果最后一行都为1的话说明是个合法方案,统计最少操作次数。
#include<iostream> #include<cstring> using namespace std; const int INF = 100000; char g[10][10]; int dx[] = {0,-1,0,1,0}, dy[] = {0,0,1,0,-1}; void turn(int x,int y)//偏移量来进行上下左右中状态的改变 { for(int i = 0;i < 5;i++) { int a = x + dx[i], b = y + dy[i]; if(a >= 0 && a < 5 && b >= 0 && b < 5) { g[a][b] = '0' + !(g[a][b] - '0'); } } } int work() { int ans = INF; for(int k = 0;k < 1 << 5;k++)//枚举第一行按哪个灯 { int res = 0; char backup[10][10]; memcpy(backup,g,sizeof g); for(int i = 0;i < 5;i++) { if(k >> i & 1) { res++; turn(0,i); } } for(int i = 0;i < 4;i++) for(int j = 0;j < 5;j++) { if(g[i][j] == '0') { res++; turn(i + 1,j); } } bool is_successful = true; for(int j = 0;j < 5;j++)//看下最后一行是否全是1 if(g[4][j] == '0') { is_successful = false; break; } if(is_successful) ans = min(ans,res); memcpy(g,backup,sizeof g);//复原数组 } if(ans > 6) return -1; return ans; } int main() { int T; cin>>T; while(T--) { for(int i = 0;i < 5;i++) cin>>g[i]; cout<<work()<<endl; } return 0; }
>奇怪的汉诺塔
在四个柱子的情况下,移动j个到b柱。之后只能放在剩下的三个柱子上,这样问题就转化为最基本的汉诺塔问题,把剩下的放到d柱子上,再把那j个放到d柱上,完成一次方案。
最基本的汉诺塔问题,是先把n-1 个圆盘放到b上,然后把最后一个圆盘放到c上,再把n-1个圆盘放到c上,完成,得出递推式为:f[n] = f[n-1] + 1 + f[n-1]
#include<iostream> #include<cstring> using namespace std; int d[13],f[13]; int main() { d[1] = 1; for(int i = 2;i <= 12 ;i++) d[i] = 1 + d[i-1] * 2;//预处理三柱模式的方案数 memset(f,0x3f,sizeof f); f[1] = 1; for(int i = 1;i <= 12;i++) for(int j = 0;j < i;j++) f[i] = min(f[i],f[j] * 2 + d[i - j]);//四柱模式下,先把j个移动到b柱,然后在三柱子模式下,把剩下的移动到目标柱,然后再把j个移动到目标柱 for(int i = 1;i <= 12;i++) cout<<f[i]<<endl; return 0; }
三个柱子的汉诺塔问题
//汉诺塔方案 #include<iostream> using namespace std; int hanoi(int step) { if(step == 1) return 1; return hanoi(step - 1) * 2 + 1; } int main() { int n; cin>>n; cout<<hanoi(n)<<endl; return 0; } //汉诺塔实现 #include<iostream> using namespace std; void hanoi(int n,char a,char b,char c) { if(n == 1) { printf("%c -> %c\n",a,c); return ; }else { //从a柱子移动到b柱 hanoi(n-1,a,c,b); //把n-1个移完之后,把最大的那个移动到c柱 printf("%c -> %c\n",a,c); //把n-1堆移动到c柱 hanoi(n-1,b,a,c); } }
这篇关于《算法进阶指南》- 0.2递推与递归的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南