蓝桥杯之递归与递推
创始人
2024-05-14 16:06:38
0

递归与递推

    • 递归实现指数型枚举?1~n任意选取
    • 递归实现排列型枚举?1~n随机打乱
    • 递归实现组合型枚举?n中选m个
      • 递归改成非递归
    • 一个数表示整个棋盘的状态?开关动否=取不取?飞行员兄弟
    • 带分数
      • 全排列分割
      • dfs嵌套,叶子节点进入另一搜索树的root节点
    • 二进制枚举每行开关状态?费解的开关

递归实现指数型枚举?1~n任意选取

递归逻辑
n的规模由n-1规模 计算得到
计算法则:
1、第n个取或不取 ( 二进制位运算 | 数组存储 )
解释为从1~n 进行枚举
二进制位运算 | 数组存储,记录取或不取的状态
2152^{15}215 = 32768
216 = 65536
220 约等于106
263 约等于 108
2、以 n=1 为基本状态,解释为情况组合

#include 
using namespace std;
int n;
void dfs(int u,int state){//2^15 = 32768 state不超过 32768
//	state第i个二进制位代表 i取不取 if(u==n+1){for(int i=1;i<=n;i++){if((state >> (i-1)) & 1){cout<cin>>n;dfs(1,0);return 0;
} 
#include 
using namespace std;
int n;
const int N=20;
bool st[N];
void dfs(int u){if(u==n+1){for(int i=1;i<=n;i++){if(st[i])cout<cin>>n;dfs(1);return 0;
} 

递归实现排列型枚举?1~n随机打乱

#include 
using namespace std;
int n;
const int N=20;
bool st[N];
int res[N];
void dfs(int u){if(u==n+1){for(int i=1;i<=n;i++){cout<//第u个数为i if(!st[i]){res[u]=i;st[i]=true;dfs(u+1);st[i]=false;}	}
}
int main(){cin>>n;dfs(1);return 0;
} 

递归实现组合型枚举?n中选m个

#include 
using namespace std;
int n,m;
const int N=20;
bool st[N];
void dfs(int u,int s){//从u开始取 u~n中,已经取了s个if(s+n-u+1for(int i=1;i<=n;i++){if(st[i])cout<n)return;
//	要求由小到大输出,那么小的数要先选 st[u]=true;dfs(u+1,s+1);st[u]=false;dfs(u+1,s);
}
int main(){cin>>n>>m;dfs(1,0);return 0;
} 
#include 
using namespace std;
int n,m;
void dfs(int u,int sum,int state){//2^15 = 32768 state不超过 32768
//	state第i个二进制位代表 i取不取 if(sum+n-u+1for(int i=1;i<=n;i++){if((state >> (i-1)) & 1){cout<cin>>n>>m;dfs(1,0,0);//从1开始 ,已经选了0个,0代表选择的状态 return 0;
} 

递归改成非递归

状态为参数保存在栈节点中 可行√

#include 
#include 
using namespace std;
int n,m;
struct node{int pos;//在递归中的位置int u,sum,state;//在栈中的内容 
};
stack stk; 
void dfs(int u,int sum,int state){//2^15 = 32768 state不超过 32768
//	state第i个二进制位代表 i取不取 
//	0:if(sum+n-u+1for(int i=1;i<=n;i++){if((state >> (i-1)) & 1){cout<cin>>n>>m;
//	dfs(1,0,0);//从1开始 ,已经选了0个,0代表选择的状态 stk.push({0,1,0,0});while(!stk.empty()){node t=stk.top();stk.pop();int u=t.u;int sum=t.sum;int state=t.state;if(t.pos==0){if(sum+n-u+1for(int i=1;i<=n;i++){if((state >> (i-1)) & 1){cout<0,u+1,sum+1,state | (1<<(u-1))});}else if(t.pos==1){t.pos=2;stk.push(t);stk.push({0,u+1,sum,state});}else continue;}return 0;
} 

状态存放在数组中 不科学× (因为递归写法,按照当前状态执行完接下来的步骤,而非递归状态,每个节点所依仗的当前状态不同,后出栈的节点等到出栈时,当前状态早已被覆盖改变)

#include 
#include 
using namespace std;
int n,m;
const int N=20;
bool st[N];
struct node{int pos;//在递归中的位置int u, s;//在栈中的内容 
};
stack stk; 
void dfs(int u,int s){//从u开始取 u~n中,已经取了s个
// 0: if(s+n-u+1for(int i=1;i<=n;i++){if(st[i])cout<n)return;
//	要求由小到大输出,那么小的数要先选 st[u]=true;dfs(u+1,s+1);
//	1:	st[u]=false;	dfs(u+1,s);
//	2:
}
int main(){cin>>n>>m;
//	dfs(0,0);stk.push({0,0,0});//第一个参数0 表示 从0位置进去while(!stk.empty()){node t=stk.top();stk.pop();int u=t.u;int s=t.s;if(t.pos==0){if(s+n-u+1for(int i=1;i<=n;i++){if(st[i])cout<n)continue;t.pos=1;stk.push(t);//继续往下走st[u]=true;stk.push({0,u+1,s+1});//相当于dfs(u+1,s+1);}else if(t.pos==1){t.pos=2;stk.push(t);//继续往下走st[u]=false;stk.push({0,u+1,s});//相当于dfs(u+1,s);}else continue;} return 0;
} 

每个递归对应一个栈

一个数表示整个棋盘的状态?开关动否=取不取?飞行员兄弟

1、用一个整数表示整个棋盘的状态
2、操作集合,暴力枚举所有动作方案
3、操作一个开关,本需要异或该行该列所有的数,利用change数组记录九个数的异或值(因为异或具有结合律,state ^ ( a1 ^ a2 ^……a9)

#include 
#include 
using namespace std;
const int N=10;
int change[N][N];
int get(int x,int y){return 4*x+y;
}
#define  PII pair
vector res;
int main(){string line;int state=0;for(int i=0;i<4;i++){cin>>line;for(int j=0;j<4;j++){if(line[j]=='+')state+=(1<<(get(i,j)));}}//计算 (i,j)开关时 影响九个值的异或值for(int i=0;i<4;i++){for(int j=0;j<4;j++){for(int k=0;k<4;k++){change[i][j]+= (1<//暴力枚举所有动作方案 int now=state;vector tmp;for(int i=0;i<16;i++){if((k>>i)&1){int x=i/4;int y=i%4;tmp.push_back({x,y});now^=change[x][y];}} if(!now && (res.size()==0 ||res.size()>tmp.size() )){res=tmp;}}cout<cout<

带分数

因为c++除法是不看小数点后面的数字的,不转为乘法会出现 n=c+b(n=c,b=0的情况,列子1=1+2/3456789,满足除法,不满足条件,所以除法的题一般都要转乘法。)

n=a+b/c,枚举a,b,c,通过该等式判断枚举的组合是否ok
除法转为乘法(带除法式子判断时可能会因为1/999=0因精度致错) nc=ac+b,

第一种枚举很巧妙,abc 充分但是不重复利用1~9 要枚举所有满足条件的abc组合
是在将1~9的所有全排列求出,对于每一种排列去枚举不同的abc分割方法(分割3段)

第二种则是两层搜索(为了减少搜索层数,只搜ac,b通过等式计算出)
在搜出a的一种情况时,在此时情况(已用1-9多少位,a的值)的基础上,去搜索c值
即在走到dfsa的搜索树 的 叶子节点 时,去到 dfsc搜索树的root结点

全排列分割

#include 
#include 
#include using namespace std;
const int N=15;
bool vis[N];
int v[N];
int n;
int cnt;
int cal(int l,int r){int value=0;for(int i=l;i<=r;i++)value=value*10+v[i];return value;
}
void dfs(int u){if(u>9){for(int i=1;i<=7;i++){//【1,9】分成3段 1~i i+1~j j+1~9 for(int j=i+1;j<=8;j++){int a=cal(1,i);int b=cal(i+1,j);int c=cal(j+1,9);//abc都不能为0,j最多只取到第8位,第9位留给c if(n*c==a*c+b){cnt++;} }}return ;}for(int i=1;i<=9;i++){if(!vis[i]){vis[i]=true;v[u]=i;dfs(u+1);vis[i]=false;		}}
}
int main(){cin>>n;
//n=a+b/c,枚举a,b,c,通过该等式判断枚举的组合是否ok 
//除法转为乘法(带除法式子判断时可能会因为1/999=0因精度致错) 
//n*c=a*c+b,dfs(1); cout<

dfs嵌套,叶子节点进入另一搜索树的root节点

#include 
#include 
#include using namespace std;
const int N=1500;
bool vis[N];
bool vis_tmp[N];
//int v[N];
int n;
int cnt;
bool check(int b){memcpy(vis_tmp,vis,sizeof(vis));while(b){int t=b%10;b/=10;if(vis_tmp[t]||t==0)return false;	//是否重复 vis_tmp[t]=true;}for(int i=1;i<=9;i++){if(!vis_tmp[i])return false;//是否充分 }return true;
}
void dfs_c(int u,int a,int c){if(u>8)return ;int b=n*c-a*c;if(!a||!b||!c){
//		return;}else{	if(check(b))cnt++;
//		return;}
//这里不要轻易return,除了9位数已经全部用完
//c的位数不确定,c要继续dfs出别的值 	for(int i=1;i<=9;i++){if(!vis[i]){vis[i]=true;dfs_c(u+1,a,c*10+i);vis[i]=false;}} 
}
void dfs_a(int u,int a){//对a的搜索,已用多少位,a的值 if(u>7)return ;//至少留两位分别给bc if(a>=n)return;//n*c=a*c+b 不能搞出负数 if(a){dfs_c(u,a,0);
//		return;a要继续dfs出别的值 }for(int i=1;i<=9;i++){if(!vis[i]){vis[i]=true;dfs_a(u+1,a*10+i);vis[i]=false;}} 
}
int main(){cin>>n;
//n=a+b/c,枚举a,b,c,通过该等式判断枚举的组合是否ok 
//除法转为乘法(带除法式子判断时可能会因为1/999=0因精度致错) 
//n*c=a*c+b,dfs_a(0,0); cout<

二进制枚举每行开关状态?费解的开关

费解在于,
很容易想到要枚举每一行的开关操作,为了点燃所有的灯(即一行一行操作下来最后要保证所有的灯燃着),自然不能 身在 iii 行而针对第 iii 行的情况去操作第 iii 行的开关,因为很显然会让上一行(第 iii 行)大乱。
因此只能通过对第 iii 行的操作需要针对的是第 i−1i-1i−1 行灯,即 通过控制第 iii 行去点燃第 i−1i-1i−1 行的灯,
自然想到,只需从第2行开始,其实不然,如果直接从第二行开始操作,那么推着推着会发现,这是一种唯一的固定的操作,永远是受牵制地按开关,而回到递推的源头——第一行的灯状态,于是,在最起初,可以枚举第一行灯的状态,以此为初态去递推,方可比较得出最小操作次数

错错错!不要看这段
这一费解点在代码中的体现是
for (int op = 0; op < 32; op ++ ) {
for (int i = 0; i < 5; i ++ ){
……
}
}
通过二进制数枚举每一行的开关状态,第1行是为了打乱第一行的灯的状态,为后面的递推创造初始状态,其余行则是为了点燃上一行的所有灯

不是暴力枚举每一行操作
而是只,暴力枚举第一行操作,第一行和其余行是分开操作的,最后一行要额外check是否全部点燃
观察 熄灯问题这种诘屈聱牙的写法也能看出(下一行的switchs由上一行决定),只有第一行的操作是通过枚举op操作的,其余行是在第一行的基础上继续进行的

#include 
#include 
#include using namespace std;
int n;
const int N=10;
const int inf=0x3f3f3f3f;
//string light[N];
//string str[N];
char  light[N][N];
char  str[N][N];
int res=inf;
int dx[5]={0,0,0,1,-1};
int dy[5]={0,1,-1,0,0};void flip(int x,int y){for(int i=0;i<5;i++){int cx=dx[i]+x;int cy=dy[i]+y;str[cx][cy]^=1;}
}
int main(){cin>>n;while(n--){res=inf;//多组输入 for(int i=0;i<5;i++){cin>>light[i];	}
//不是暴力枚举每一行操作
//暴力枚举第一行操作		for(int op=0;op<(1<<5);op++){memcpy(str,light,sizeof(light));//头文件 string.h ,老写错成strcpy int cnt=0;//i=0 第1行 for(int j=0;j<5;j++){//第1行 if((op>>j)&1){flip(0,j);cnt++;} }//i=1~4  剩余行 for(int i=1;i<5;i++){for(int j=0;j<5;j++){if(str[i-1][j]=='0'){//千万不能写成 !str[i-1][j]  啊  flip(i,j);cnt++;}}}//check最后一行 bool bright=true;for(int j=0;j<5;j++){if(str[4][j]=='0'){bright=false;break;}} if(bright){res=min(res,cnt);}}if(res>6)cout<<"-1";else cout<

相关内容

热门资讯

迷你退出安卓系统了吗,转型新篇... 最近有没有发现你的手机上那个可爱的迷你退出图标突然不见了?别急,让我来给你揭秘迷你退出安卓系统了吗的...
华为优先使用安卓系统,打造自主... 你知道吗?最近科技圈里有个大动作,那就是华为宣布优先使用安卓系统。这可不是一个简单的决定,它背后可是...
安卓系统隐藏了设置,隐藏设置功... 你知道吗?安卓系统这个大宝藏里,竟然隐藏着一些不为人知的设置!是不是听起来就有点小激动呢?别急,今天...
反渣恋爱系统安卓,收获真爱 你有没有听说过那个神奇的“反渣恋爱系统安卓”呢?最近,这款应用在网络上可是火得一塌糊涂,不少单身狗都...
安卓出厂系统能升级,探索无限可... 你知道吗?现在这个时代,手机更新换代的速度简直就像坐上了火箭!而说到手机,安卓系统可是占据了半壁江山...
老安卓刷机系统,从入门到精通 你有没有想过,你的老安卓手机其实还有大大的潜力呢?没错,就是那个陪伴你多年的老安卓,它可不是只能用来...
安卓粉ios系统app,兼容性... 你有没有发现,身边的朋友圈里,安卓粉和iOS系统粉总是争论不休?今天,咱们就来聊聊这个话题,看看安卓...
安卓系统语言下载,探索安卓系统... 你有没有想过,你的安卓手机是不是该换换口味了?没错,就是语言!想象如果你能轻松切换到自己喜欢的语言,...
安卓共有多少种系统,究竟有多少... 你有没有想过,安卓这个我们每天不离手的操作系统,竟然有那么多不同的版本呢?没错,安卓系统就像一个大家...
安卓系统怎么播放swf,And... 你有没有遇到过这种情况:手里拿着一部安卓手机,想看一个SWF格式的动画,结果发现怎么也打不开?别急,...
pos机安卓系统跟win系统,... 你有没有想过,那些在我们生活中默默无闻的POS机,竟然也有自己的操作系统呢?没错,就是安卓系统和Wi...
俄罗斯封禁安卓系统,本土化替代... 俄罗斯封禁安卓系统的背后:技术、经济与社会的影响在数字化浪潮席卷全球的今天,智能手机已成为我们生活中...
安卓系统总是弹出权限,安卓系统... 手机里的安卓系统是不是总爱和你玩捉迷藏?每次打开一个应用,它就跳出来问你要不要给它开权限,真是让人又...
安卓系统测血氧,便捷健康生活新... 你知道吗?现在科技的发展真是让人惊叹不已!手机,这个我们日常生活中不可或缺的小玩意儿,竟然也能变身成...
蓝光助手安卓系统的,深度解析与... 你有没有发现,现在手机屏幕越来越大,看视频、刷抖音,简直爽到飞起!但是,你知道吗?长时间盯着屏幕,尤...
安卓系统如何隐藏提示,Andr... 你是不是也和我一样,在使用安卓手机的时候,总是被那些弹出来的提示信息打扰到?别急,今天就来教你怎么巧...
安卓6.0系统如何分区,And... 你有没有想过,你的安卓手机里那些神秘的分区到底是怎么来的?别急,今天就来给你揭秘安卓6.0系统如何分...
安卓系统图片怎么涂鸦,指尖上的... 你有没有想过,在安卓系统的手机上,那些单调的图片也能变得生动有趣呢?没错,就是涂鸦!今天,就让我来带...
安卓系统40g,40GB存储空... 你有没有发现,最近你的安卓手机突然变得有点“胖”了呢?没错,就是那个传说中的40G!别急,别慌,今天...
安卓5.0系统怎么重置,轻松实... 手机用久了是不是感觉卡得要命?别急,今天就来教你怎么给安卓5.0系统来个彻底的重置,让它焕发新生!一...