Acwing 蓝桥杯备战(第一天 递归)

本文详细介绍了如何利用递归算法解决指数型、排列型和组合型枚举问题,以及带分数的计算。通过递归树的概念,展示了在不同场景下如何构建递归函数以生成所有可能的解决方案。此外,还探讨了如何在全排列基础上解决更复杂的三连击问题,即找到满足特定条件的数字组合。
摘要由CSDN通过智能技术生成

递归实现指数型枚举

请添加图片描述思路分析
一共n位数字和n个位置,每个位置上每个数字都有选与不选两种情况,建成一颗递归树递归遍历输出每一种情况的答案。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n;
int a[N];
void dfs(int x){
    if(x>n){
        for(int i=1;i<=n;i++){
            if(a[i]) printf("%d ",i);
        }
        printf("\n");
    }
    else{
        a[x]=1; //表示选择该数字
        dfs(x+1); //递归到下一个位置
        
        a[x]=0; //表示不选择该数字
        dfs(x+1); //递归到下一个位置
    }
}
int main(){
    cin>>n;
    dfs(1);
    return 0;li
}

递归实现排列型枚举

请添加图片描述

思路分析

额外开辟一个新的vis数组,表示每个数字是否被使用,遍历每个位置,选择未被使用的数字放置在该位置上,并标记该数字已被使用,递归遍历下一位,特别注意“恢复现场”

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int a[N],vis[N];
int n;
void dfs(int x){
    if(x>n){
        for(int i=1;i<=n;i++){
            printf("%d ",a[i]);
        }
        printf("\n");
    }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;
}

递归实现组合型枚举

请添加图片描述

思路分析
类似于实现排列型枚举,但不同的是需要选择m个数字,并且出现顺序不同的算成同种方案。

#include<bits/stdc++.h>
using namespace std;
const int N=30;
int a[N],vis[N];
int n,m;
void dfs(int x){
    if(x>m){
        for(int i=1;i<=m;i++){
            printf("%d ",a[i]); //此时要选择m个数字
        }
        printf("\n");
        return;
    }
    else{
        for(int i=a[x-1]+1;i<=n;i++){
            //每次遍历的开始是上一次被选择的数字后边的一个数保证不出现重复
            if(!vis[i]){
                a[x]=i;
                vis[i]=1;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
}
int main(){
    cin>>n>>m;
    dfs(1);
    return 0;
}

带分数

请添加图片描述思路分析
要求n=a+ c b \frac{c}{b} bc,即得到nb==ab+c,实现1-9的全排列,从中截断出a,b,c三个整数,对于符合条件的整数cnt++递归结束得到正确答案。

#include<bits/stdc++.h>
using namespace std;
const int N=15;
int t[N],vis[N];
int n,cnt;
int num(int l,int r){
    // 将数组中的数字合并成一个大整数
    int sum=0;
    for(int i=l;i<=r;i++){
        sum+=t[i];
        if(i!=r) sum*=10;
    }
    return sum;
}
void dfs(int x){
    int a,b,c;
    if(x==10){
        for(int i=1;i<=9;i++){
            for(int j=i+1;j<=9;j++){
                a=num(1,i);
                b=num(i+1,j);
                c=num(j+1,9);
                if(n*b*1ll==a*b*1ll+c&&a&&b&&c) cnt++;
            }
        }
        return;
    }else{
        for(int i=1;i<=9;i++){
            // 实现全排列
            if(!vis[i]){
                t[x]=i;
                vis[i]=1;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
}
int main(){
    cin>>n;
    dfs(1);
    printf("%d",cnt);
    return 0;
}

思路2
递归遍历a,b计算出c,再对c进行判断,看c是否符合条件

#include<bits/stdc++.h>
using namespace std;
int n;
int a[10],vis[10],backup[10];
int cnt;
bool check(int a,int c){
	long long b=n*(long long)c-a*c;
	if (!a || !b || !c) return false;
	memcpy(backup,vis,sizeof vis);
	while(b){
		int x=b%10;
		b/=10;
		if(!x||backup[x]==1) return false;
		backup[x]=1;
	}
	for(int i=1;i<=9;i++){
		if(!backup[i]) return false;
	}
	return true;
}
void dfs_c(int x,int a,int c){
	if(x>9){
		return;
	}
	if(check(a,c)) cnt++;
	for(int i=1;i<=9;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs_c(x+1,a,c*10+i);
			vis[i]=0;
		}
	}
}
void dfs_a(int x,int a){
	if(a>=n) return;//a大于等于n时,属于无解的情况
	if(a) dfs_c(x,a,0);
	for(int i=1;i<=9;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs_a(x+1,a*10+i);
			vis[i]=0;
		}
	}
}
int main(){
	cin>>n;
	dfs_a(1,0);
	printf("%d",cnt);
	return 0;
}

三连击(升级版)

请添加图片描述思路分析
找出1-9的全排列,分割成三个数字,输出符合条件的组合。

#include<bits/stdc++.h>
using namespace std;
const int N=15;
int a[N],vis[N];
int A,B,C;
int cnt;
void dfs(int x){
	if(x==10){
		int num1=a[1]*100+a[2]*10+a[3];
		int num2=a[4]*100+a[5]*10+a[6];
		int num3=a[7]*100+a[8]*10+a[9];
		
		if(num1*B*C==num2*A*C&&num1*B*C==num3*A*B&&num2*A*C==num3*A*B){
			cnt++;
			printf("%d %d %d\n",num1,num2,num3);
		}
		
		return;
	}
	else{
		for(int i=1;i<=9;i++){
			if(!vis[i]){
				a[x]=i;
				vis[i]=1;
				dfs(x+1);
				vis[i]=0;
			}
		}
	}
}
int main(){
	cin>>A>>B>>C;
	dfs(1);
	if(!cnt) printf("No!!!\n");
	return 0;
}

思路二
根据题意 num1BC== num2AC == num3AB
因此,可以根据等式先计算出num3,遍历枚举num1和num2找出符合条件的组合。

#include<bits/stdc++.h>
using namespace std;
const int N=10;
int vis[N],backup[N];
int A,B,C;
int cnt;
bool check(int num1,int num2){
	int num3=num1*C/A;
	memcpy(backup,vis,sizeof vis);
	if(num1*B*C!=num2*A*C) return false;
	while(num3){
		int x=num3%10;
		num3/=10;
		if(!x||backup[x]) return false;
		backup[x]=1;
	}
	for(int i=1;i<=9;i++){
		if(!backup[i]) return false;
	}
	return true;
}
void dfs_c(int x,int num1,int num2){
	if(x>6){
		if(check(num1,num2)){
			cnt++;
			printf("%d %d %d\n",num1,num2,num1*C/A);
		} 
		return;
	}
	for(int i=1;i<=9;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs_c(x+1,num1,num2*10+i);
			vis[i]=0;
		}
	}
}
void dfs_a(int x,int num1){
	if(x>3){
		dfs_c(x,num1,0);
		return;
	}
	for(int i=1;i<=9;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs_a(x+1,num1*10+i);
			vis[i]=0;
		}
	}
}
int main(){
	cin>>A>>B>>C;
	dfs_a(1,0);
	if(!cnt){
		printf("No!!!");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值