算法---回溯(正文)

1.什么是回溯?

回溯算法的定义就是和暴力枚举一样枚举所有可能并加撤回,也能和暴力一样去掉一些重复(在之前就被筛出,但还要枚举这个,我们可以跳过这个了---------这个就是回溯剪枝)。但为什么回溯不是暴力呢?----这个问题大家可以先想想最后我在说。

其实回溯也是递归,如果你熟悉树状图的话,你会发现回溯的枚举过程就是一个树,而递归呢也是一棵树在这里插入图片描述

2.“回溯”该怎么做?

回溯顾名思义就是撤回,走到头也要像递归一样终止(如果到头的结果对了储藏答案反之否)。如果没走到头则产生多个子节点继续探索。
根据上面的解释回溯的代码应为

void dfs(变量){if(终止条件){存放结果 return ; //结束 }枚举所有可行 
}

当然这是最基本的模版,并没用到回溯
下面是标准模板

int flag[105];//标记 1 yes 2 no
void dfs(变量){if(终止条件){if(判断是否存放结果){存放 }return ;} for(枚举){if(flag[i]==0)   //判断是否被标记过{flag[i]=1; //标记 存放数据 dfs(变量+1);flag[i]=0; //回溯 } } 
} 

3.回溯经典问题

全排列

输入正整数N,输出由1到N这N个数(N<=7)的所有排列,每行一个排列,数与数之间有一个空格,两个排列中,第一个数小的优先输出,第一个数相同,比较第二个数,后面以此类推。

输入
正整数N

输出
所有排列

样例
输入 1
3
输出 1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
【分析】题目比较简单,模板一带就行

#include<bits/stdc++.h>
using namespace std;
int n;
int a[105],flag[105];
void dfs(int dep){   //判断到第几层了if(dep==n+1){   for(int i=1;i<=n;i++){  //输出cout<<a[i]<<" ";}cout<<"\n";return ;}for(int i=1;i<=n;i++){if(flag[i]!=1){flag[i]=1;a[dep]=i;dfs(dep+1);flag[i]=0;  //回溯}}
} 
int main(){
cin>>n;
dfs(1);return 0;
}

组合数的拆分

【题目描述】
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。

现要求你用递归的方法输出所有组合。

例如n=5,r=3,所有组合为:

1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5

【输入】
一行两个自然数n、r(1<n<21,1≤r≤n)。

【输出】
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

【输入样例】
5 3
【输出样例】
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
【分析】与全排列只能说如出一辙,只需注意排序顺序

int ab[30];
int flag[30];
int n,r;
void dfs(int dep,int last){   //底几层   上一个数+1是啥if(dep == r+1){for(int i = 1 ; i < dep ; i++){cout << setw(3) << ab[i];}cout << "\n";return ;}for(int i = last ; i <= n ; i++){  //从上一个数+1开始枚举if(flag[i] == 0){flag[i] = 1;ab[dep] = i;dfs( dep+1 , i+1 );flag[i] = 0;}}
}
int main()
{
cin >> n >> r;
dfs(1,1);return 0;
}

LETTERS

【题目描述】
给出一个row×col
的大写字母矩阵,一开始的位置为左上角,你可以向上下左右四个方向移动,并且不能移向曾经经过的字母。问最多可以经过几个字母。

【输入】
第一行,输入字母矩阵行数R
和列数S
,1≤R,S≤20

接着输出R
行S
列字母矩阵。

【输出】
最多能走过的不同字母的个数。

【输入样例】
3 6
HFDFFB
AJHGDH
DGAGEH
【输出样例】
6
【分析】这个题目是回溯+递归(深搜),不过难度不大有方向数组就行

#include<bits/stdc++.h>
using namespace std;
int row,col; 
char ab[25][25];
int flag[27];
int dx[]={0,0,-1,1}; //方向数组
int dy[]={1,-1,0,0};
int rc[25][25],cnt=0;
void dfs(int x,int y,int ans){  //当前位置    探索了多少cnt=max(ans,cnt);for(int i=0;i<4;i++){int xd=dx[i]+x;int yd=dy[i]+y;if(flag[int(ab[xd][yd]-'A')]==0&&rc[xd][yd]==0&&xd>=1&&xd<=row&&yd>=1&&yd<=col){flag[int(ab[xd][yd]-'A')]=1;rc[xd][yd]=1;dfs(xd,yd,ans+1);flag[int(ab[xd][yd]-'A')]=0;  //回溯rc[xd][yd]=0;}}return;
}
int main()
{
cin>>row>>col;
for(int i=1;i<=row;i++){for(int j=1;j<=col;j++){cin>>ab[i][j];}
}
flag[int(ab[1][1]-'A')]=1; //刚开始的被探索过了
rc[1][1]=1;
dfs(1,1,1);
cout<<cnt;return 0;
}

N皇后

在8*8国际象棋盘上,放置8个皇后,使得任意两个皇后都不会互相攻击,一共有多少种摆法?这就是著名的8皇后问题。

现在我们来尝试,在N*N的棋盘上,放置N个皇后,使得它们不会互相攻击。

皇后的走子规则是,沿着横、竖、两条对角线方向可以走任意步数。

输入
1个整数N

输出
一个整数,表示N皇后的不同解答个数

样例
输入 1
8
输出 1
92
5<=n<=9
【分析】毋庸置疑回溯,他要判断三项,那我们想,我们是不是可以不用关行了,因为我们可以枚举每一行,然后可以用回溯解决列,那斜线呢,当然也能拿数组去回溯。可是当你去回溯时,怎么做呢?
我们不妨句例子下
4皇后
(1,1)(1,2)(1,3)(1,4)
(2,1)(2,2)(2,3)(2,4)
(3,1)(3,2)(3,3)(3,4)
(4,1)(4,2)(4,3)(4,4)
当我们打上正反斜线,发现什么了?每个斜线上的各店的x+y互相相等----正斜线
每个斜线上的x与y的差(abs(x-y))互相相等----反斜线
所以我们可以用俩个数组标记俩斜线

#include<bits/stdc++.h>
using namespace std;
int n;
int lie[12],ans,xie1[100],xie2[100]; //标记数组
void dfs(int dep){if(dep==n+1){ans++;return ;} for(int i=1;i<=n;i++){if(lie[i]==0&&xie1[dep+i]==0&&xie2[dep-i+10]==0){  //如果dep-i<0,+10会让dep-i>0因为n<=9lie[i]=1;xie1[dep+i]=1;xie2[dep-i+10]=1;dfs(dep+1);lie[i]=0;xie1[dep+i]=0;xie2[dep-i+10]=0;}}
}
int main()
{
cin>>n;
dfs(1);
cout<<ans;return 0;
}

造素数

现在给你n个数,你需要从中选出m个数,使得这m个数的和为素数,求出可选的方案数。

输入
第一行两个整数n和m。

第二行n个整数,表示可选的数字。

输出
输出有多少种方案可以使得选出的数之后为素数。

样例
输入 1
3 2
1 2 3

输出 1
2

输入 2
3 1
2 2 2
输出 2
3
这里不做分析了原理同组合数的拆分

#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
int a[105],b[105];
bool isprime(int n){if(n==1) return false;for(int i=2;i*i<=n;i++){if(n%i==0) return false;}return true;
}
void dfs(int cnt,int now){if(now==n+1){if(cnt==m){int k=0;for(int i=0;i<cnt;i++){k+=b[i];}if(isprime(k)){ans++;}}return ;}b[cnt]=a[now];dfs(cnt+1,now+1); //选dfs(cnt,now+1); //不选
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
dfs(0,1);
cout<<ans<<"\n";return 0;
}

最后解答一下开始时的问题
在这几个例题中,相信大家已经看出来了暴力需要好几层,而虽然回溯也是和暴力差不多的时间复杂度可是却可以用少量代码解决要几个for才能解决的问题(有的时候也是解决不了的如LETTERS)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/463078.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux系统体验】-archlinux折腾日记

archlinux 一、系统安装二、系统配置及美化2.1 中文输入法2.2 安装virtualbox增强工具2.3 终端美化 三、问题总结3.1 一、系统安装 安装步骤人们已经总结了很多很全: Arch Linux图文安装教程 大体步骤&#xff1a; 磁盘分区安装 Linux内核配置系统&#xff08;基本软件&…

C#使用哈希表对XML文件进行查询

目录 一、使用的方法 1.Hashtable哈希表 2.Hashtable哈希表的Add方法 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;示例 3.XML文件的使用 二、实例 1.源码 2.生成效果 可以通过使用哈希表可以对XML文件进行查询。 一、使用的方法 1.Hashtable哈希表…

2024-02-08(Flume)

1.Flume 的架构和MQ消息队列有点类似 2.Flume也可以做数据的持久化操作 在Channel部分选择使用File channel组件 3.Flume进行日志文件监控 场景&#xff1a;企业中应用程序部署后会将日志写入到文件中&#xff0c;我们可以使用Flume从各个日志文件将日志收集到日志中心以便…

深入理解java之多线程(一)

前言&#xff1a; 本章节我们将开始学习多线程&#xff0c;多线程是一个很重要的知识点&#xff0c;他在我们实际开发中应用广泛并且基础&#xff0c;可以说掌握多线程编写程序是每一个程序员都应当必备的技能&#xff0c;很多小伙伴也会吐槽多线程比较难&#xff0c;但因为其实…

【Web】基于Mybatis的SQL注入漏洞利用点学习笔记

目录 MyBatis传参占位符区别 不能直接用#{}的情况 in多参数值查询 like %%模糊查询 order by列名参数化 MyBatis传参占位符区别 在 MyBatis 中&#xff0c;#{} 和 ${} 都是用于传参的占位符&#xff0c;但它们之间有很大的区别&#xff0c;主要体现在两个方面&#xff1a…

鸿蒙开发(六)布局概述

迄今为止&#xff0c;我还没有正式提到布局的概念。但其实我之前的demo里面&#xff0c;已经默认使用到了一种布局&#xff0c;那就是线性布局&#xff08;Row、Column&#xff09;&#xff0c;这也是DevEco创建项目默认页面里面默认采用的布局。那么本篇&#xff0c;带着大家一…

[SAP] ABAP设置非系统关键字代码提示功能

在事务码SE38(ABAP编辑器)屏幕右下角&#xff0c;点击【Options选项】图标 勾选【代码完成】|【建议文本中的非关键字】&#xff0c;并点击【保存】按钮 在下面的程序代码中&#xff0c;当我需要输入在11行的位置输入非关键字lv_str的时候&#xff0c;会有非关键字代码提示的功…

Python速成篇(基础语法)下(新年快乐♥)

引言 一天不学编程手就痒&#xff0c;今天是除夕&#xff0c;学C艹vector的话就没时间出去玩了&#xff0c;所以就写写博客。今天要讲的内容是关于&#xff0c;list&#xff08;列表&#xff09;&#xff0c;tuple&#xff08;元组&#xff09;&#xff0c;字典&#xff08;di…

【MySQL进阶之路】磁盘随机读写和顺序读写对MySQL性能的影响

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

CSP-202009-1-称检测点查询

CSP-202009-1-称检测点查询 解题思路 本题的时间复杂度貌似没有限制&#xff0c;直接暴力枚举就能知识盲点&#xff1a;sort()函数-升序排序 #include <algorithm>给名为dis&#xff0c;长度为n的数组排序sort(new_dis, new_dis n); #include <iostream> #inc…

【c语言进阶】自定义类型-枚举+联合(共用体)详解

目录 1.枚举 1.1枚举类型的定义 1.2枚举常量的创建和初始赋值 2.枚举的优点 3.枚举的使用 4. 联合&#xff08;共用体&#xff09; 4.1 联合类型的定义 4.2 联合体的特点 4.3联合体大小的计算 4.4联合体的运用-判断程序的大小端存储 5.结语 1.枚举 枚举顾名思义就…

JAVA设计模式之建造者模式详解

建造者模式 1 建造者模式介绍 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式. 定义: 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 **建造者模式要解决的问题 ** 建造者模式可以将部件和其组装过程分开…