回溯法经典例题

1.装载问题:(cw当前装载重量; bestw当前最优装载重量; c剩余集装箱重量)

void backtrack(int t)
{
    if(t>n)//1下含一for循环,遍历叶子结点
    {
        for(j=1;j<=n;j++)
        {
            bestx[j]=x[j];
            bestw=cw;
        }
    }
    return;
    r-=w[i];//4
    if(cw+w[i]<=c)
    {
        w[i]=1;
        cw+=w[i];
        backtrack(t=1);
        cw-=w[i];
    }
    if(cw+c>bestw)
    {
        w[i]=0;
        backtrack(t+1);
    }
    r+=w[i];
}

*2.批处理作业调度问题(Tij作业i在机器j的处理时间;Fij作业i在机器j上完成处理的时间;)

void backtrack(int t)
{
    if(t>n)
    {
        for(j=1;j<=n,j++)
        {
            bestx[j]=x[j];
            bestf=f;
        }
    }
    else
        for(j=i;j<=n;j++)
        {
            f1+=m[x[i]][j];
            f2[i]=((f2[i-1]>f1)?f2[i-1];f1)+m[x[j]][2];//f2+=f1;
            f+=f2[i];
        }
        if(f<bestf)
        {
            swap(x[i],x[j]);
            backtrack(i+1);
            swap(x[i],x[j]);
        }
        f1-=m[x[i]][j];
        f-=f2[i];
}

3.符号三角形问题

+、-个数均为n(n+1)/4个

void backtrack(int t)
{
    if(count>half||t(t+1)/4-count>half) return;//注此例题约束条件放到了叶子结点检验前
    if(t>n)
    sum++;//处在叶子结点时
    for(i=0;i<=1;i++)//0表+,1表-,此循环0、1一次性展开
    {
        p[1][t]=i;
        count+=i;
        for(j=2;j<=n;j++)
        {
            p[j][n-j+1]=p[j-1][n-j+1]+p[j-1][n-j+2];
            count+=p[j][n-j+1;
        }
        backtrack(t-1);
        for(j=2;j<=n;j++)
        {
            count-=p[j][n-j+1;
            count-=i;
        }
    }
}

*4.n皇后问题

约束条件:不同行

可行解:不同列,不同对角线(即|i-j|=|x[i]-x[j]|)

boolean place(int k)
{
    for(i=1;i<=k;i++)
    if(math.abs(x[i]-x[j])==math.abs(i-j)||x[i]==x[j]) return false;//判断同列,同对角线
    else true;
}//place函数
void backtrack(int t)
{
    if(t>n)
    sum++;
    else
        for(i=1;i<=n;i++)
        {
          x[t]=i //第t行皇后处于i列, x[i]=t;
           if(place(t))
           backtrack(k+1);//判定place不行,并继续执行子树
        }
}

*5.0-1背包问题(类似装载问题)

注:在搜索解空间树时,只要左儿子结点是一个可行结点,搜索就进入左子树

当右子树可能包含最优解时才进入右子树搜索,否则剪去

*bound()计算当前结点初的上界,当要进入右子树时才计算bound

void backtrack(int i)
{
    if(I>n)
    {
        bestp=p;//当前结点即最大值
        return;
    }
    //搜索子树
    if(cw+w[i<=c])//是一个可行结点,进入左子树
    {
        cw+=w[i];//
        cp+=p[i];//
        backtrack(i+1);
        cw-=w[i];
        cp-=p[i];
    }
    if(bound(i+1)>bestp)//可能包含最优解,才进入右子树,否则不进入/bestp>=bestw
    backtrack(i+1);//
}
double bound(int i)
{   //计算上界,贪心法
    double cleft=c-cw;//cleft剩余容量,c背包容量,cw 当前重量
    double bound=cp;//bound上界,cp当前价值
    //以物品单位价值递减顺序装入背包
    while(i<=n||w[i]<=cleft)
    {
        cleft-=w[i];
        bound+=p[i];
        i++;
    }
    //装满背包
    if(i<=n)
    bound+=p[i]*cleft/w[i];//切割部分装入背包
    return bound;
}


    

6.最大团问题(无向连通图中任意两点有连接边 所含顶点数最多的团)

void backtrack(int i)//当前扩展结点
{
    if(i>n)
    {
        for(j=1;j<=n;j++)
            bestx[j]=x[j];//记录最优解
            bestn=cn;//记录当前最大团包含点数
        return;
    }
    //可行性约束函数
    Boolean ok=true;
    for(j=1;j<=n;j++)
        if(x[j]==1&&!a[i][j])//i与j不连接
        {
            ok=false;
            break; 
        }   
    if(ok)//进入左子树
    {
        x[i]=1;//路径信息
        cn++;
        backtrack(t+1);
        cn--;
    }
    if(cn+n-i>bestn)//进入右子树
    {
        x[i]=0;
        backtrack(t+1);
    }
}

7.图的m着色问题

问题描述:无向连通图G需m种颜色使图中每两条边连接的两个顶点着色不同,求最小m值

可行性约束条件:顶点i与已着色的相连顶点颜色不同,不同颜色用x[i]表示

void backtrack(int t)
{
    if(t>n)
    {
       sum++;
    }
    else
        for(i=1;i<=m;i++)//从根节点开始依次展开所有的m个分支
        {
            x[t]=i;//分支信息赋值给x[t]
            if(ok(t)=true backtrack(t+1))//判定第t个点选着第i种颜色是否可行
         }
}
boolean ok(int k)
{
    for(intj=1;j<=k,j++)
    if(a[i][k]&&x[i]==x[k])//已用过该颜色
        return falsed;
}//检查颜色可用性

8旅行售货员问题

问题描述:售货员到若干城市去推销商品,已知各城市之间的路程,要求经过每个城市一遍,最后回到住地的路线,使总的路程最短。

9.圆排列问题

11.电路板排列问题(T11、12偏难,会比较啰嗦)

12.连续邮资问题

问题描述:假设某国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。连续邮资问题要求对于给定的n和m,给出邮票面值的最佳设计,在1张信封上贴出从邮资1开始,增量为1的最大连续邮资区间。
例n=5,m=4时,面值为1,3,11,15,32的5种邮票可以贴出邮资的最大连续区间是1到70。
题目意思即在5种邮票种任选一到四张(可重复选择面值相同的邮票),用选出的邮票面值总和的数值可能出现的情况是1到70的连续区间

比如选择一张1,数值为1;选两1,数值和为2;一张3or3张1,数值和为3;一张3和一张1or四张1,数值和为4;一张3和两种1数值和为5,两张3数值和为6;两张3和一张1数值和为7;...一张32+一张15+两张11=69;两张32+两张3=70注:此段浅灰段的内容,是为了辅助理解题意,鄙人自己补充的,与题目无关,读不通勿喷

问题的解:n种面值从小到大排序,用x[1:n]表示 

13.排列树搜索(3类)

①n个不同元素的全排列
②有n个重复元素的全排列
③n个不同元素去镜像的全排列(不含镜像的排列n!/2个元素)

算法设计:选定好首元素,再选尾元素,然后确定中间n-2个元素的全排列

排列数搜索总结:
①去镜像搜索时间耗费降低为原来的1/2
②一个n元素集合,有K个元素重复,重复度是P1,P2,…PK,

则无重复排列数是n!/(p1!p2!...pk!)
③回溯法之排列树,根据问题考虑是否需去重复,去镜像
注:backtrack(t) 中t为当前结点要扩展的下层的编号 ,用for()循环控制各方发的展开
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值