第12章回溯算法

回溯算法

一种递归的进化,回溯算法是一个大的算法框架

要学编码技巧和优化技巧

使用递归实现的搜索算法

递归:关注递归程序的实现,不关注解决什么问题—出拳

回溯:解决问题,针对某一类明确的问题场景–咏春拳

理解回溯法:用深度优先搜索遍历问题求解树,加上剪枝进行优化

先有后优:先实现后优化,切勿妄想一步到位

在这里插入图片描述

洛谷 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])


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厨 神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值