回溯算法

回溯法是一种类似于穷举的解决方式

思路:遍历所有可选择元素或者数据,如果当前选择不符合问题要求就会产生回溯,即抛弃当前的选择回到上一个状态并进行其他的选择。

 

回溯法的两个例子

装载问题:

n个集装箱装入载重量为W的轮船,第i个集装箱的重量为w[i],要求轮船装的质量最多,在装载质量相同的情况下,要求在质量相同的情况下,装载的数量最少,使用回溯法解决该问题。

 

此问题可分解为可数个决策(对于某个集装箱的选与不选),通过遍历决策的布尔代数(0|1)产生了问题解空间的满二叉树,所以这个问题是通过遍历二叉树实现遍历问题的所有解,当决策脱离了问题的要求就“回溯”,直到得到满足问题要求的解的决策。回溯法是对决策树的遍历。

使用回溯法,遍历集装箱,使用minx[],保存装载的最优序列,

1、保证最优=》在重量最重和当前集装箱树<min时,更新集装箱序列。

2、裁剪条件=》当前集装箱数>min时或者当前重量>轮船载重时,直接回溯,

3、T(o) = O(n^2)

#include<stdio.h>
#define MAX 20
int n,W;//轮船的容量 
int maxw;//最优解的重量 
int minm;//存放集装箱个数 
int x[MAX];//最优解选择序列
//装载回溯遍历(质量序列,物品数,当前选择的物品,物品总重量,当前选择序列,当前选择的数目)
void loading(int w[],int tw,int m,int op[],int i){
    int j;
    if(i>n){
        if(tw<=W&&(tw>maxw||(tw==maxw&&m<minm))){
            maxw = tw;
            minm = m;
            for(j=1;j<=n;j++)
                x[j] = op[j];
        }
    }else{
        op[i] = 1;
        if(tw+w[i]<=W)
            loading(w,tw+w[i],m+1,op,i+1);
        //回溯 
        op[i] = 0;
        if(m<=2)
            loading(w,tw,m,op,i+1);
    }
} 

void display(int n){
    printf("选择的集装箱:\n");
    for(int i=1;i<=n;i++)
        if(x[i]==1)
            printf("选择第%d个集装箱\n",i);
    printf("\n");
}

int main(){
    //初始化轮船 
    n = 5;W = 10;
    //集装箱质量
    int w[] = {0,5,2,1,4,3};
    //物品选择数组
    int op[MAX];
    loading(w,0,0,op,1);
    display(n);
    return 0;
}
装载问题

全排列问题

这个问题体现了回溯的另一个特点,由于数组是引用变量,回溯时数组元素没有还原到未进行选择前的状态,所以加了一条语句进行还原。体现了回溯要保持和元数据相同。

#include<stdio.h>
void perm(char s[],int k,int n){
    int i;
    char tmp;
    if(k==n-1){
        for(i=0;i<n;i++)
            printf("%c",s[i]);
        printf("\n");
    }else{
        for(i=k;i<n;i++){
            tmp = s[k];s[k] = s[i];s[i] = tmp;//交换s[k]与s[i] 
            perm(s,k+1,n);
            tmp = s[k];s[k] = s[i];s[i] = tmp;//交换s[k]与s[i],恢复环境即回溯。 
        }
    }
}
int main(){
    int n=3;
    char s[] = "123";
    perm(s,0,n);
}
全排列

 

posted @ 2017-06-04 18:16  _春华秋实  阅读(187)  评论(0)    收藏  举报