回溯算法及其优化过程

 如何理解回溯算法?

如果一个问题能够变成问题状态搜索树,那么深搜的过程就是用回溯求解的过程。

做法:1.先写出问题状态搜索树。

2.先实现再优化。

例题1:八皇后问题:

我们就按行顺序取图上这些棋子的坐标,发现:

这就是在求排列形枚举。不同的是,有一些排列要舍去(还要满足对角线)

先贴上原来的代码:

void Print(int n,int cur){
    if(cur==n+1) {
        for (int i = 1; i <= n; i++)printf("%d", ans[i]);
        printf("\n");return;
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            ans[cur]=i;
            vis[i]=1;
            Print(n,cur+1);
            vis[i]=0;
        }
    }
}

优化①:状态压缩:在输出全排列数组的时候,我们使用了一个vis数组,vis【i】=1代表i元素被占用了。可以用一个二进制数代替这个数组。这样把数组用数字替代。

优化②:快速枚举:

之前枚举的时候,是对所有i从1到n枚举,其中也包括一些已经被占用了的数字。由此我们想到,可以用位运算,直接取二进制数字最小的非0位置。(方法,x&(-x),可以证明求得的就是x不为0的最小位权)然后枚举完后,再用x-=x&(-x)

优化③:斜边表示:

本题其实是有限制版本的”全排列枚举“。我们发现x+y-1=i,表示在第i条正邪斜边的棋子,所以只要多取两个二进制数即可。

例题2:

优化1:剪枝操作:对每一个节点,我们假设中间有一点C,假设现在已知A-->C只用x步;那么当我们发现有一种方法是A-->C用x+1步时,就没有遍历的必要了。因为它不可能是最优解。由此判断。

例题3:马的遍历

本题有点类似二维的例题二。

优化1:本题每个点马会有八个选择,用方向数组表示:

例题4:迷宫

变成技巧:扩展棋盘+合法化

这样可以同时实现1.判断障碍物。2.判断坐标是否合法 3.判断是否走过一次

例题5:

优化1:分析问题搜索树:

又是排列形枚举!!所以优化1肯定是状态压缩。

优化2:搜索过程剪枝:

对于形如这样的两组(最后的终点都一样,且到目前为止的效果都一样),可以根据值的大小舍去。所以我们想到用一个dp【】【】数组,dp[t][6]代表第t个效果,6代表末尾位置是6.

#include<bits/stdc++.h>
using namespace std;
double x[20],y[20];
double dis[20][20];
double dp[70000][20];//dp用来记录状态
double ans=1e6;
double p_dis(int p1,int p2){
    return sqrt((x[p1]-x[p2])*(x[p1]-x[p2])+(y[p1]-y[p2])*(y[p1]-y[p2]));
}
void dfs(int t1,int now,double s){//t1是状态码,s是当前的距离。
if(t1==0){
    if(s<ans)ans=s;
    return;
}//如果已经拍完了。
for(int k=t1;k;k-=(k&-k)){
    int t=(k&-k);int to=0;
    while(1){
        if(1<<to==t)break;
        to++;//to就是当前吃掉的奶酪编号
    }
    int next=t1-(1<<to);//吃掉to后的状态码
    if(dp[next][to]!=0&&dp[next][to]<s+dis[now][to])continue;
    dp[next][to]=s+dis[now][to];
    if(ans<s+dis[now][to])continue;
    dfs(next,to,s+dis[now][to]);
}
}
int main(){
    int n;
    scanf("%d",&n);
    x[0]=y[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%lf %lf",&x[i],&y[i]);
    }
    for(int i=0;i<=n;i++){
        for(int j=i;j<=n;j++){
            dis[i][j]=dis[j][i]=p_dis(i,j);//计算出个距离
        }
    }
    dfs(((1<<(n+1))-2),0,0);
    printf("%.2f",ans);
return 0;
}

 思考本题要解决的问题:

1,扩展路径判断:存在重叠部分

2.维护龙的长度:

1.2可用预处理d【x】【y】表示两单词x,y重叠长度。用L+LEN(Y)-d【x】【y】

3.控制单词使用次数:可用一个vis数组表示使用次数。(注意回溯时候要减去使用次数)

例题6:拆分自然数:

问题搜索树:

优化1:定义拆分数字要逐渐增大。

优化2:判断拆分可行性

dfs(层号,可以使用的起始数字,还剩下的数字)

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值