回溯算法
一种递归的进化,回溯算法是一个大的算法框架
要学编码技巧和优化技巧
使用递归实现的搜索算法
递归:关注递归程序的实现,不关注解决什么问题—出拳
回溯:解决问题,针对某一类明确的问题场景–咏春拳
理解回溯法:用深度优先搜索遍历问题求解树,加上剪枝进行优化
先有后优:先实现后优化,切勿妄想一步到位

洛谷 P1219 [USACO1.5] 八皇后 Checker Challenge
在nxn棋盘放n个皇后(不同行不同列),问有多少个方案
是否在一条直线上:t=[0,0,0,…0,0]:是否访问 : t-=(-t &t )
是否在斜边:
正斜边:i+j-1 :t2=[0,0,0,…0,0]
反斜边:i-j+n:3=[0,0,0,…0,0]
排列形枚举 但消除不要的排序
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time : 2024/4/12 16:58
# @Author : Cry
# @File : 八皇后问题.py
# @IDE : PyCharm
class BaHuanghou:
def __init__(self):
self.s=0
self.n=0
self.a=[0]*1000 # 是否为一行
self.b=[0]*1000 # 是否为一列
self.c=[0]*1000 #是否为正斜边 行+列和相同
self.d=[0]*1000 #是否为反斜边 列-行差相同
def print_one(self):
self.s+=1
if(self.s<=3):
for i in range(1,(self.n)+1):
print(self.a[i],end="")
if(self.n-i):
print(" ",end="")
print()
def search(self,i):
n=self.n
for j in range(1,n+1):
if(self.b[j]==0 and self.c[i+j]==0 and self.d[i-j+n]==0):
self.a[i]=j;
self.b[j]=1;
self.c[i+j]=1
self.d[i-j+n]=1;
if(i==n):
# 满足条件输出
self.print_one()
else:
self.search(i+1)
# 回溯
self.b[j]=0
self.c[i+j]=0
self.d[i-j+n]=0
def run(self):
self.n=int(input())
self.search(1);
print(self.s)
BaHuanghou().run()
c++
//
// Created by cry on 2024/4/11.
//
#include <iostream>
using namespace std;
#include <unordered_map>
#define MASK(n) ((1<<(n+1)) -2)//掩码
int total_ans=3;
int acm_12_huisu_BaHuanghou_ind_arr[20];
unordered_map<int,int> acm_12_huisu_BaHuanghou_ind;
void print_one_result(int n){
//输出一组结果
for(int i=0;i<n;i++){
if(i)printf(" ");
printf("%d",acm_12_huisu_BaHuanghou_ind_arr[i]);
}
printf("\n");
total_ans-=1;
return;
}
int acm_12_huisu_BaHuanghou_dfs(int i,int t1,int t2,int t3,int n){
if(i>n){ //n行都放了皇后
if(total_ans) print_one_result(n);
return 1;
}
int ans=0;
for(int t=t1;t;t-=(-t & t)){
int j=acm_12_huisu_BaHuanghou_ind[-t & t]; //当前二进制状态表示中的1
if((t2 & (1 <<(i+j-1))) && (t3 &(1 <<(i-j+n)))){ //正反斜线都可以使用
acm_12_huisu_BaHuanghou_ind_arr[i]=j;
// printf("%d:%d\n",i,j);
ans+=acm_12_huisu_BaHuanghou_dfs(i+1,t1^(1<<j),t2^(1<<(i+j-1)),t3^(1<<(i-j+n)),n);
}
}
return ans;
}
void acm_12_huisu_BaHuanghou_test(){
int n;
scanf("%d",&n);
//回溯法是基于递归函数的
total_ans=3;
for(int i=0;i<2*n;i++) acm_12_huisu_BaHuanghou_ind[1<<i]=i;
int ans=acm_12_huisu_BaHuanghou_dfs(0,MASK(n),MASK(2*n-1),MASK(2*n-1),n);
printf("%d\n",ans);
}
排列形枚举,要求输出
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time : 2024/4/12 20:47
# @Author : Cry
# @File : 枚举型枚举.py
# @IDE : PyCharm
st=[0]*10
n=3
path=[0]*10
def dfs(u):
# 边界条件
if(u>n):
for i in range(1,n+1):
print(path[i],end=" ")
print()
return ;
for i in range(1,n+1):
if(st[i]==False):
path[u]=i
st[i]=True
# 递归
dfs(u+1)
st[i]=False #回溯
dfs(1)
快速枚举:
-t & t
6543210
1001100 :只有632可以用:-t &t能取得二进制表示中最低为1: 4:2^2=4
枚举完最低为后:减去就好了
t -= (-t &t)
洛谷P1135 奇怪的电梯
输入:
5 1 5 3 3 1 2 5代表一共5层楼,求从第1层到第5层最少按几次按钮
使用历史答案剪枝
//
// Created by cry on 2024/4/12.
//
#include <iostream>
using namespace std;
#define MAX_N 200
int acm_12_huisu_QiguaideDianti_dis[MAX_N +5]={0};
int acm_12_huisu_QiguaideDianti_to[MAX_N+5];
void acm_12_huisu_QiguaideDianti_dfs(int k,int a,int n){
if(acm_12_huisu_QiguaideDianti_dis[a]<=k)return;
acm_12_huisu_QiguaideDianti_dis[a]=k;
if(a+acm_12_huisu_QiguaideDianti_to[a]<=n) acm_12_huisu_QiguaideDianti_dfs(k+1,a+acm_12_huisu_QiguaideDianti_to[a],n);
if(a-acm_12_huisu_QiguaideDianti_to[a]>=1) acm_12_huisu_QiguaideDianti_dfs(k+1,a-acm_12_huisu_QiguaideDianti_to[a],n);
return ;
}
void acm_12_huisu_QiguaideDianti_test(){
int n,a,b;
scanf("%d%d%d",&n,&a,&b);
for (int i=1;i<=n;i++) scanf("%d",acm_12_huisu_QiguaideDianti_to+i);
for(int i=1;i<=n;i++){
acm_12_huisu_QiguaideDianti_dis[i]=n+1;
}
acm_12_huisu_QiguaideDianti_dfs(0,a,n);
printf("%d\n",acm_12_huisu_QiguaideDianti_dis[b]==n+1?-1:acm_12_huisu_QiguaideDianti_dis[b]);
}
void acm_12_huisu_QiguaideDianti_test();
int main() {
acm_12_huisu_QiguaideDianti_test();
return 0;
}
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time : 2024/4/12 23:33
# @Author : Cry
# @File : 奇怪的楼梯.py
# @IDE : PyCharm
n,a,b=map(int,input().split())
k=[0]+list(map(int,input().split()))
dis=[n+1]*(n+1)#起点a到达终点b的已知的最短距离,默认极大值n+1
def dfs(s,a): # a表示当前在的点 b表示终点
# 历史答案剪枝
# 如果当前搜索到的到达这个点的距离,没有比之前已知的答案更优秀的时候
# 就结束搜索
if(dis[a] <= s): #如果已知dis[a] <= k 没有意义,直接返回
return
dis[a]=s #κ小于dis[a]
#分成左右两边 往上走和往下走 进行搜索
if(a+k[a] <=n): #往上走
dfs(s+1,a+k[a])
if(a-k[a]>=1): #往下走
dfs(s+1,a-k[a])
return;
# 回溯搜索
dfs(0,a) #到达当前点用了几步, 当前在的点 , 终点 ,总的节点数量n
if dis[b]==n+1: #到达终点的距离为默认值最大值
print(-1) #到不了
else: #可以达到
print(dis[b])
组合型枚举
#include <iostream>
using namespace std;
void acm_12_huisu_zuheXingMeiJu_dfs(int k,int ind,int n,int m){
if(k==m){
//完成了
return;
}
for(int i=ind;i<=n;i++){
//select i
acm_12_huisu_zuheXingMeiJu_dfs(k+1,i+1,n,m);
}
}
void acm_12_huisu_zuheXingMeiJu_test(){
int n,k;
cin >> n>>k;
acm_12_huisu_zuheXingMeiJu_dfs(0,1,n,k); //选了0个,这一层第一个为1,可选数字范围为n,最多选k个
}
HZOJ 236 递归实现组合型枚举
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time : 2024/4/13 0:55
# @Author : Cry
# @File : 组合型枚举.py
# @IDE : PyCharm
m, n = map(int, input().split())
path = [0] * (n + 1) #第i层的数字
def dfs(u, ind):
if (u == n+1):
print(*path[1:]) #输出一次递归结果
return
for i in range(ind, m + 1): #从当前层的最小值到 输入的最大值
if path[u-1] < i: #保证当前层大于上一层
path[u] = i
dfs(u + 1, ind + 1) #继续下一层
dfs(1, 1) #层数初始为1,最小值为1
P1036 选数
//
// Created by cry on 2024/4/13.
//
#include <iostream>
using namespace std;
#define MAX_N 20
int acm_12_huisu_zuheXingMeiJu_val[MAX_N+5];
long long ans=0;
int is_prime(int x){
for(int i=2;i*i<=x;i++){
if(x %i==0)return 0;
}
return 1;
}
void acm_12_huisu_zuheXingMeiJu_dfs(int k,int ind,int n,int m,int sum){
if(k==m){
//选择了k个数字,和值为sum
if (is_prime(sum)){ //判断和值是否为素数
ans+=1;
}
return;
}
for(int i=ind;i<=n;i++){
//select i
acm_12_huisu_zuheXingMeiJu_dfs(k+1,i+1,n,m,sum+acm_12_huisu_zuheXingMeiJu_val[i]+sum);
}
return;
}
void acm_12_huisu_zuheXingMeiJu_test(){
int n,k;
cin >> n>>k;
for(int i=1;i<=n;i++){
scanf("%d",acm_12_huisu_zuheXingMeiJu_val+i);
}
acm_12_huisu_zuheXingMeiJu_dfs(0,1,n,k,0); //选了0个,这一层第一个为1,最多选k个,当前选择的数字的和值
printf("%lld\n",ans);
}
int main() {
acm_12_huisu_zuheXingMeiJu_test();
return 0;
}
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time : 2024/4/13 0:43
# @Author : Cry
# @File : 选数.py
# @IDE : PyCharm
n,k=map(int,input().split())
x=[0]+list(map(int,input().split()))
def is_prime(x):
i=2
while i*i<=x:
if x%i==0:
return False
i+=1
return True
def dfs(u, ind, sum):
if(u==k):
if is_prime(sum):
ans[0]+=1
return ;
for i in range(ind,n+1):
dfs(u+1,i+1,sum+x[i])
ans = [0]
dfs(0,1,0)
print(ans[0])
@IDE : PyCharm
n,k=map(int,input().split())
x=[0]+list(map(int,input().split()))
def is_prime(x):
i=2
while i*i<=x:
if x%i==0:
return False
i+=1
return True
def dfs(u, ind, sum):
if(u==k):
if is_prime(sum):
ans[0]+=1
return ;
for i in range(ind,n+1):
dfs(u+1,i+1,sum+x[i])
ans = [0]
dfs(0,1,0)
print(ans[0])

865

被折叠的 条评论
为什么被折叠?



