10.3学习内容(递归、搜索与回溯、全排列)

发布于:2022-12-01 ⋅ 阅读:(224) ⋅ 点赞:(0)

递归

例题1:集合的划分

代码:

#include<iostream>
using namespace std;
long long dfs(int n,int k){
	if(n<k||k==0) return 0;
	if(n==k||k==1)return 1;
	return dfs(n-1,k-1)+k*dfs(n-1,k);
}
int main(){
	int n,k;
	cin>>n>>k;
	cout<<dfs(n,k);
	return 0;
}

【题目解析】

在放第n个数时有两种情况,一为并入前面k个集合中,情况有k*dfs(n-1,k)种,二为自成一个集合,情况为dfs(n-1,k-1)种,所以所有的情况就为dfs(n-1,k-1)+k*dfs(n-1,k)种。

例题2:逆波兰表达式

代码:

#include<iostream>
#include<iomanip>
#include<bits/stdc++.h> 
using namespace std;
char a[10010];
double f(){
	scanf("%s",&a);
	if(a[0]=='*')return f()*f();
	else if(a[0]=='/')return f()/f();
	else if(a[0]=='+')return f()+f();
	else if(a[0]=='-')return f()-f();
	else{
		return atof(a);
	}
}
int main(){
	cout<<fixed<<setprecision(6)<<f();	
	return 0;
}

【题目解析】

通过定义没有参数的函数来获取输入并对输入进行操作,分别对读入的字符型数据进行处理,如果为符号则不断向后层递归,如果为数字则利用atof函数转换为浮点型数据再进行运算。

补充拓展:

atof与atoi

头文件:#include<stdlib.h>

atof(字符数据)——》double双精度浮点数据

atoi(字符数据)——》int整型数据

例题3:全排列(字母)

代码:

#include<iostream>
using namespace std;
string a,b;
int k;
bool vis[10];
void dfs(int m){
	if(m==k){
		for(int i=0;i<k;i++){
			cout<<b[i];
		} 
		cout<<endl;
	}
	else{
		for(int i=0;i<k;i++){
			if(!vis[i]){
				vis[i]=1;
				b[m]=a[i];
				dfs(m+1);
				vis[i]=0;
			}
		}
	}
} 
int main(){
	cin>>a;
	k=a.size();
	dfs(0);
}

【题目解析】

dfs深搜,回溯。

附题:全排列(字母)

代码:

#include<iostream>
using namespace std;
int a[10],n;
bool vis[10];
void dfs(int x){
	if(x==n+1){
		for(int i=1;i<=n;i++){
			printf("%5d",a[i]);
		}
		cout<<endl;
	}
	else{
		for(int i=1;i<=n;i++){
			if(!vis[i]){
				a[x]=i;
				vis[i]=1;
				dfs(x+1);
				vis[i]=0;
			}
		}
	}
}
int main(){
	cin>>n;
	dfs(1);
	return 0;
}

例题4:数的计数

代码:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int cnt=0,h[1010];
void dfs(int x){
	if(h[x]!=-1)return ;
	h[x]=1;
	for(int i=1;i<=x/2;i++){
		dfs(i);
		h[x]=h[x]+h[i];
	}
}
int main(){
	memset(h,-1,sizeof(h));
	int n;
	cin>>n;
	dfs(n);
	cout<<h[n];
	return 0;
}

【题目解析】

从1到x/2不断遍历深搜,h数组记录已经计算过的当前数的情况,优化了时间复杂度。

补充拓展:

memset():

头文件#include<string>

memset(a,0,sizeof(a))使数组a的所有元素初始化为0。

搜索与回溯

搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题
的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回 一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。

 

例题1:素数环

题目简介:

素数环:从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数

代码:
#include<iostream>
using namespace std;
int n,a[25],cnt=0;
bool vis[25];
bool ss(int x){
	bool fl=1;
	for(int i=2;i<=n/i;i++){
		if(x%i==0){
			return 0;
		}
	}
	return fl;
}
void dfs(int m){
	if(m==n+1&&ss(a[m]+a[1])){
		cnt++;
		for(int i=1;i<=n;i++){
			cout<<a[i]<<" ";
		}
		cout<<endl;
	}
	else{
		for(int i=2;i<=n;i++){
			if(!vis[i]&&ss(a[m-1]+i)){
				a[m]=i;
				vis[i]=1;
				dfs(m+1);
				vis[i]=0;
			}
		}
	}
}
int main(){
	cin>>n;
	a[1]=1;
	vis[1]=1;
	dfs(2);
	cout<<cnt;
	return 0;
}

【题目分析】

分析:

因为1~20摆成一个环总数太多,我们选择1~10,并且第一个位置我们定为1。

例题2:自然数的拆分

代码:
#include<iostream>
using namespace std;
int a[10];
int n;
void dfs(int m,int s){
	if(s==0){
		cout<<n<<"="<<a[1];
		for(int i=2;i<m;i++){
			cout<<"+"<<a[i];
		}
		cout<<endl;
	}else{
		for(int i=1;i<n;i++){
			if(i>=a[m-1]&&(i<=s-i||s==i)){
				a[m]=i;
				dfs(m+1,s-i);
			}
		}
	}
}
int main(){
	cin>>n;
	dfs(1,n);
	return 0;
}

【题目解析】

if(i>=a[m-1]&&(i<=s-i||s==i)):

例题3:八皇后问题

代码:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int a[20],cnt=0;
bool b[20],zx[20],yx[20];
void dfs(int i){
	if(i==9){
		cnt++;
		cout<<"No."<<" "<<cnt<<endl;
		for(int i=1;i<=8;i++){
			for(int j=1;j<=8;j++){
				if(a[j]==i)cout<<"1"<<" ";
				else cout<<"0"<<" ";
			}
			cout<<endl;
		}
	}else{
		for(int j=1;j<=8;j++){
			if(!b[j]&&!zx[i+j]&&!yx[i-j+8]){
				a[i]=j;
				b[j]=1;
				zx[i+j]=1;
				yx[i-j+8]=1;
				dfs(i+1);
				b[j]=0;
				zx[i+j]=0;
				yx[i-j+8]=0;
			}
		}
	}
}
	
int main(){
	dfs(1);
//	cout<<cnt;
	return 0;
}

【题目解析】

例题4:马的遍历

例题5:选数

代码:

#include<iostream>
using namespace std;
int n,k,a[30],cnt=0;
bool ss(int x){
	bool fl=1;
	for(int i=2;i<=x/i;i++){
		if(x%i==0){
			fl=0;
			break;
		}
	}
	return fl;
}
void dfs(int l,int m,int s){
	if(m==k+1){
		if(ss(s))cnt++;
	}else{
		for(int i=l;i<=n;i++){
			dfs(i+1,m+1,s+a[i]);
		}
	}
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	dfs(1,1,0);
	cout<<cnt;
	return 0;
} 

【题目解析】

从i+1位不断递归,避免选过的数重新被选。

课后训练:

训练1:LETTERS        

代码参考:

#include<iostream>
using namespace std;
int n,m;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
char a[25][25];
bool v[200];
int nmax=-1e9;
void dfs(int s,int x,int y){
	if(s>nmax)nmax=s;
	for(int i=0;i<=3;i++){
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(!v[a[xx][yy]]&&xx>=1&&xx<=n&&yy>=1&&yy<=m){
			v[a[xx][yy]]=1;
			dfs(s+1,xx,yy);
			v[a[xx][yy]]=0;
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	v[a[1][1]]=1;
	dfs(1,1,1);
	cout<<nmax;
	return 0;
}

【题目解析】

一条路深搜,记录经过最多字母数的路径,用bool数组v标记已经走过的字母,避免重复经过。

训练2:迷宫

参考代码:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int ha,la,hb,lb,n;
bool v[105][105],fl=0;
char a;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
bool in(int x,int y){
	if(x>=0&&x<n&&y>=0&&y<n)return 1;
	else return 0;
}
void dfs(int x,int y){
//	cout<<" heha"<<x<<y<<endl;
	for(int i=0;i<4;i++){
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(in(xx,yy)&&!v[xx][yy]){
			v[xx][yy]=1;
			if(xx==hb&&yy==lb){
	  			cout<<"YES"<<endl;
				fl=1;
				break;
			}else{
				dfs(xx,yy);
			}
//			v[xx][yy]=0;
		}
	}
}
int main(){
	int k;
	cin>>k;
	for(int l=1;l<=k;l++){
//		cout<<l<<" ";
		fl=0;
		memset(v,0,sizeof(v));
		cin>>n;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				cin>>a;
				if(a=='#')v[i][j]=1;	
			}
		}
		cin>>ha>>la;
		cin>>hb>>lb;
		if(v[ha][la]||v[hb][lb]){
			cout<<"NO"<<endl;
			continue;
		}else{
	//		cout<<"hehahh";
			dfs(ha,la);
		}
		if(!fl){
			cout<<"NO"<<endl;
		}
	} 
	return 0;
}


 
【题目解析】
 dfs深搜

网站公告

今日签到

点亮在社区的每一天
去签到