回溯类题目总结

对于回溯法的理论描述这个就不赘述了,可以参考下面几个文章:
https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html
https://blog.csdn.net/EbowTang/article/details/51570317

这里主要说一下回溯法基本写法和步骤(以八皇后问题为例):

void queen(int now, int all){ //all = 8, row代表当前在规划第row行的皇后
    for(int i = 1; i <= all; i++){
        if(check_ok(now, i, all)){
            arr[now][i] = 1; //表示第row行的皇后放在第i列
            if(now == all) //这是最后一个皇后
                ++sum; //总方案数+1
            else
                queen(now+1, all); // 递归调用,摆放下一个
            arr[now][i] = 0; //第now个皇后摆在这个位置的所有相关方案已经全部尝试完毕,恢复原貌
        }
    }
}
  1. 每层函数要循环枚举这次要做的事情的所有情况,例如,每次摆一个皇后进去的时候,要枚举它在这一行的所有可能位置(for(int i = 1; i <= all; ++i)

2.检查要做的事情的合理性,如检查第now个皇后摆放在这里会不会和已有的皇后冲突(check_ok(now, i, all)

  1. 函数func的每一层递归一定会做一些事情,不会不做任何事情,如每次进入一层函数,一定会摆一个皇后进一个格子。(arr[row] = i;)

  2. 回溯法有递归的思想,可以以函数func的递归来写。(queen(row+1, all);

  3. 每次退出函数时,一定要恢复操作前的状态(arr[now][i] = 0;)

例一:N皇后问题(杭电OJ 2553 http://acm.hdu.edu.cn/showproblem.php?pid=2553

这道题有个小trick,因为数据范围小N<=10,所以要提前计算好然后打表,否则会超时

#include <iostream>
#include <string.h>

using namespace std;
int arr[11][11];
int sum = 0;

bool check_ok(int row, int col, int all){
    for(int i = 1; i < row; ++i) //同一列没有其他
        if(arr[i][col] == 1)
            return false;
    for(int i = row - 1, j = col - 1; i >= 1 && j >= 1; --i, --j){ //左斜线
        if(arr[i][j] == 1)
            return false;
    }
    for(int i = row - 1, j = col + 1; i >= 1 && j <= all; --i, ++j){ //右斜线
        if(arr[i][j] == 1)
            return false;
    }
    return true;
}

void queen(int now, int all){
    for(int i = 1; i <= all; i++){ //遍历所有情况
        if(check_ok(now, i, all)){ //检查合理性
            arr[now][i] = 1; //操作
            if(now == all)
                ++sum; //结束
            else
                queen(now+1, all); //递归
            arr[now][i] = 0; //还原操作
        }
    }
}

int res[11];
int main(){
    int n;
    for(int i = 1; i <= 10; ++i){
        sum = 0;
        memset(arr, 0, sizeof(int) * 11 * 11);
        queen(1, i);
        res[i] = sum;
    }
    while(1){
        cin >> n;
        if(n == 0)
            break;
        cout << res[n] << endl;
    }
    return 0;
}

写法2, 基本差不多,省点内存:

#include <iostream>
#include <string.h>

using namespace std;

int arr[11], sum = 0;

bool check_ok(int row, int i, int all){
    for(int j = 1; j < row; ++j){
        if(i == arr[j] || i - arr[j] == row - j || i - arr[j] == j - row)
            return false;
    }
    return true;
}

void queen(int row, int all){
    for(int i = 1; i <= all; ++i){
        if(check_ok(row, i, all)){
            arr[row] = i;
            if(row == all)
                ++sum;
            else
                queen(row+1, all);
        }
    }
}

int res[11];
int main(){
    int n;
    for(int i = 1; i <= 10; ++i){
        sum = 0;
        memset(arr, 0, sizeof(int) * 11);
        queen(1, i);
        res[i] = sum;
    }
    while(1){
        scanf("%d", &n);
        if(n == 0)
            break;
        printf("%d\n", res[n]);
    }
    return 0;
}

例二:生成括号(LeetCode22 https://leetcode-cn.com/problems/generate-parentheses/description/

#include <iostream>
#include <string>
#include <string.h>
#include <vector>

using namespace std;

class Solution {
public:
    int left_right;
    vector<string> res;
    char* tmp;

    vector<string> generateParenthesis(int n) {
        left_right = 0;
        tmp = new char[2 * n + 1];
        memset(tmp, 0, sizeof(char) * (2 * n + 1));
        generate(0, 2 * n);
        delete tmp;
        return res;
    }

    void save_str(){
        res.push_back(string(tmp));
    }

    void generate(int now, int all){ //注意可行操作只有左右括号两种,因此没有循环,而是写了两次
        tmp[now] = '('; //操作
        ++left_right;
        if(now == all - 1)
            ;
        else
            generate(now+1, all); //递归调用
        --left_right;
        tmp[now] = 0; //还原操作

        if(left_right > 0){ //只有此时左括号多于右括号时,才能加入右括号
            --left_right;
            tmp[now] = ')';
            if(now == all - 1){
                if(left_right == 0)
                    save_str();
            }
            else{
                generate(now+1 ,all);
            }
            ++left_right;
            tmp[now] = 0;
        }
    }
};

例三:整数分解(PAT-A1103 https://www.patest.cn/contests/pat-a-practise/1103

#include <iostream>
#include <string.h>
#include <vector>

using namespace std;

vector<int> arr;
vector<int> res;
int n, k, p;
int i = 1;
int goods[21];
int max_sum = 0;

bool do_print(int sum){
    if(sum > max_sum){
        max_sum = sum;
        return true;
    }
    return false;
}

void func(int which, int volumn, int count, int sum){
    for(int a = which; a >= 1; --a){ // 遍历所有情况
        if(goods[a] <= volumn){
            arr.push_back(a);  //将a计算在内
            sum += a; //所有因子的和
            if(goods[a] == volumn && count - 1 == 0){
                if(do_print(sum)){
                    res.clear();
                    res.assign(arr.begin(), arr.end());
                }
            }
            else if(count > 0){
                func(a, volumn - goods[a], count - 1, sum); //递归调用
            }
            sum -= a;
            arr.pop_back(); //删除a
        }
    }
}

int main(){
    cin >> n >> k >> p;
    while(1){
        int temp = 1;
        for(int j = 0; j < p; ++j){
            temp = temp * i;
        }
        if(temp > n)
            break;
        goods[i++] = temp;
    }
    i--;
    func(i, n, k, 0);
    if(res.empty()){
        cout << "Impossible" << endl;
    }
    else{
        int size = res.size();
        cout << n << " = ";
        for(int a = 0; a < size; a++){
            cout << res[a] << "^" << p;
            if(a != size - 1)
                cout << " + ";
        }
        cout << endl;
    }
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容