返回顶部

『嗨威说』算法设计与分析 - 回溯法思想小结(USACO-cha1-sec1.5 Checker Challenge 八皇后升级版)

本文索引目录:

一、回溯算法的基本思想以及个人理解

二、“子集和”问题的解空间结构和约束函数

三、一道经典回溯法题点拨升华回溯法思想

四、结对编程情况

 

 

一、回溯算法的基本思想以及个人理解

  1.1  基本概念:

    回溯法思路的简单描述是:把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。

 

  1.2  使用条件:

    当问题是要求满足某种性质(约束条件)的所有解或最优解时,便可以使用回溯法,其实有暴力剪枝的意味

 

  1.3  使用思想:

    回溯法常常使用DFS(深度优先搜索)来进行对解空间的答案搜索。

    首先从根根部节点出发搜索解空间,当搜索至解空间的某一节点时,先利用约束或限界剪枝函数来判断该节点是否是问题的解。如果不是,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。

    回溯法的基本行为是优雅的暴力搜索,搜索过程进行剪枝来为了避免无效冗余的搜索。

    剪枝主要包括两类:

      1. 使用约束函数,剪去不满足约束条件的路径;

      2.使用限界函数,剪去不能得到最优解的路径。

 

  1.4  回溯算法的经典问题种类:

    (1)装载问题
    (2)0-1背包问题
    (3)旅行售货员问题
    (4)八皇后问题
    (5)迷宫问题
    (6)图的m着色问题

 

 

二、“子集和”问题的解空间结构和约束函数

    (1)解空间结构
        非负非零整数集合S={x1,x2,…,xn}对应的的一个子集S1,S1中的元素之和为c。

    (2)约束函数
        若当前数总和加上下一个数,大于题目所给数时,我们则选择不要这个数。

        用now表示不算已选择的元素的其他元素的值得总和, aim为题目所需要的输入的和的总值,

        now+temp[i]<=aim;

        在代码中可以直接用分支判断进入递归,也可以在递归头进行返回判断,在这里我使用递归头直接判断

        即:if(i>=times || flag == 1) return; flag在分支中进行标记判断是否大于aim值

 

 

三、一道经典回溯法题点拨升华回溯法思想

  3.1  题目来源:

    USACO-cha1-sec1.5 Checker Challenge

    http://www.nocow.cn/index.php/Translate:USACO/checker

 

  3.2  题目题干:

Checker Challenge

    Examine the 6x6 checkerboard below and note that the six checkers are arranged on the board so that one and only one is placed in each row and each column, and there is never more than one in any diagonal. (Diagonals run from southeast to northwest and southwest to northeast and include all diagonals, not just the major two.)

    The solution shown above is described by the sequence 2 4 6 1 3 5, which gives the column positions of the checkers for each row from 1 to 6:

        ROW 1 2 3 4 5 6
        COLUMN 2 4 6 1 3 5
    This is one solution to the checker challenge. Write a program that finds all unique solution sequences to the Checker Challenge (with ever growing values of N). Print the solutions using the column notation described above. Print the the first three solutions in numerical order, as if the checker positions form the digits of a large number, and then a line with the total number of solutions.

    Special note: the larger values of N require your program to be especially efficient. Do not precalculate the value and print it (or even find a formula for it); that's cheating. Work on your program until it can solve the problem properly. If you insist on cheating, your login to the USACO training pages will be removed and you will be disqualified from all USACO competitions. YOU HAVE BEEN WARNED.

 

    INPUT FORMAT

A single line that contains a single integer N (6 <= N <= 13) that is the dimension of the N x N checkerboard.
SAMPLE INPUT (file checker.in)
6

 

    OUTPUT FORMAT

The first three lines show the first three solutions found, presented as N numbers with a single space between them. The fourth line shows the total number of solutions found.
SAMPLE OUTPUT (file checker.out)
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

 

  3.3  题目大意:

    题目意思是:

      一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
      找出所有棋子放置的解。并把它们以上面的序列方法输出,解按字典顺序排列。

 

  3.4  题目思路:

    八皇后题型,非常典型的回溯法解题

    对于一条从右上到左下的对角线,其上的棋子坐标应满足x+y为一定值;

    对于一条从左上到右下的对角线,其上的棋子坐标应满足x-y为一定值,为了避免负数的产生,代码中用x-y+times来储存数字。

    对于语句

    if( check[0][i] != 1 )
      if( check[1][t+i] != 1)
        if( check[2][t-i+times] != 1 )

    只要满足这三个数字均为使用过,则在temp[line]=i处放置棋子,并将check数组中的相应数值标记为已使用,并对下一行进行搜索。

    由于题目要求输出前3组解,所以计数器cnt>3时不输出结果,最后在main函数中输出最终解的数量。

 

  3.5  题目AC代码:

#include<bits/stdc++.h>
using namespace std;

int times,ans,cnt,temp[99],check[99][99];
void back(int t)
{
    if(t > times)
    {
        cnt++;
        if(cnt>3)
            return ;
        for(int i = 1;i<=times;i++)
            cout<<temp[i]<<" ";
        cout<<"\n";
        return ;
    }
    
    for(int i = 1;i<=times;i++)
    {
        if( check[0][i] != 1 )
            if( check[1][t+i] != 1)
                if( check[2][t-i+times] != 1 )
                {
                    temp[t] = i;
                    check[0][i] = check[1][t+i] = check[2][t-i+times] = 1;
                    back(t+1);
                    check[0][i] = check[1][t+i] = check[2][t-i+times] = 0;
                }
    }
}
int main()
{
    cin>>times;
    back(1);
    cout<<cnt;
    return 0;
}

 

 

四、 结对编程情况:

    一学期的合作打题还算是友好合作型,在共同研究算法以及在思维碰撞的过程中找到合理的解题思路,相互梳理的过程是很nice的一个过程。

    这或许就是ACMer结对比赛的一种魅力吧,有时候一个人的力量常常不如一个团队的力量来的直接,希望以后在各个项目的合作过程中都能遇到像三木这么nice的小哥哥(商业互吹)。

 

 

如有不合理的地方,请及时指正,我愿听取改正~

参考链接:https://oi-wiki.org/

posted @ 2019-12-12 17:41  嗨威er  阅读(371)  评论(0编辑  收藏  举报