回溯刷题总结

回溯总结

组合问题

509. 斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

class Solution {
public:
    int reverse(int n){
        if(n<2) return n;
        return reverse(n-1)+reverse(n-2);
    }
    int fib(int n) {
        return reverse(n);
    }
};
\77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

class Solution {
public:
    vector<vector<int> > result;
    vector<int> path;
    void backtracking(int n, int k,int index){
        if(path.size()==k){
            result.push_back(path);
            return;
        }
        for(int i = index;i<=n;i++){
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return result;
    }
};
一层递归就是一层for,看代码顺序先遍历到满足k的最后一个数,然后依次向上返回判断逻辑
2611 老鼠奶酪

有两只老鼠和 n 块不同类型的奶酪,每块奶酪都只能被其中一只老鼠吃掉。

下标为 i 处的奶酪被吃掉的得分为:

如果第一只老鼠吃掉,则得分为 reward1[i] 。
如果第二只老鼠吃掉,则得分为 reward2[i] 。
给你一个正整数数组 reward1 ,一个正整数数组 reward2 ,和一个非负整数 k 。

请你返回第一只老鼠恰好吃掉 k 块奶酪的情况下,最大 得分为多少

class Solution {
public:
    int sum(vector<int>& num){
        int sum =0;
        for(auto c:num) sum+=c;
        return sum;
    }
    vector<int> path;
    int sum_max=0;
    void backtracking(vector<int>& reward1, vector<int>& reward2,int& sum_2, int& k,int index){
        if(path.size()==k){
            sum_max = max(sum_max,sum(path)+sum_2);
            return;
        }
        for(int i =index;i<reward1.size();i++){
            path.push_back(reward1[i]-reward2[i]);
            backtracking(reward1,reward2,sum_2,k,i+1);
            path.pop_back();
        }
    }
    int miceAndCheese(vector<int>& reward1, vector<int>& reward2, int k) {
        int sum_2 = sum(reward2);
        backtracking(reward1,reward2,sum_2,k,0);
        return sum_max;
    }
};
这道题目中,需要通过数学公式,把两个数组抽象为1个,然后进行组合的回溯
1010. 总持续时间可被 60 整除的歌曲

在歌曲列表中,第 i 首歌曲的持续时间为 time[i] 秒。

返回其总持续时间(以秒为单位)可被 60 整除的歌曲对的数量。形式上,我们希望下标数字 i 和 j 满足 i < j 且有 (time[i] + time[j]) % 60 == 0。

class Solution {
public:
    int numPairsDivisibleBy60(vector<int>& time) {
        int vec[61] = {};
        int result=0;
        for(int i=0;i<time.size();i++){
            int tmp = time[i];
            result+=vec[(60-tmp%60)%60];
            vec[tmp%60]++;
        }
        return result;
    }
};
本题回溯时间复杂度太高,考虑通过数组存一个遍历,将问题转化为单变量问题
两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        unordered_map<int,int> dict;
        for(int i=0;i<nums.size();i++){
            int tmp = nums[i];
            if(dict[target-tmp]!=0){
                result.push_back(i);
                result.push_back(dict[target-tmp]-1);
            }
            dict[tmp] = i+1;
        }
        return result;
    }
};
在通过遍历某一变量,进行变量降维时,核心思想是建立一个值到个数的索引。如果知道元素的个数,可以用数组。如果元素个数不好处理,直接用哈希表。其中map.count(a)不会增加计数,返回bool值

切割问题

131.分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]

class Solution {
public:
    vector<string> path;
    vector<vector<string>> result;
    void backtracking(string& s,int index){
        if(index>=s.size()){
            result.push_back(path);
            return ;
        }
        for(int i = index;i<s.size();i++){
            string str = s.substr(index,i-index+1);
            if(adjust(str)){
                path.push_back(str);
                backtracking(s,i+1);
                path.pop_back();
            }
            
        }
    }
    bool adjust(string& a){
        int i = 0;
        int j = a.size()-1;
        while(i<j){
            if(a[i]!=a[j]) return false;
            i++;
            j--;
        }
        return true;
    }
    vector<vector<string>> partition(string s) {
        backtracking(s,0);
        return result;
    }
};
在回溯的分割任务中,index可以理解为分割的起始位置,而外部循环的i的后面为隔板,概念类似于end。所以要分割的第一个串即为index到i的字符。代码为s.substr(index,i-index+1),第二个参数为要选择的元素个数
并且在判断回文的时候,由于需要对首尾指针进行数值判断,所以j为s.size()-1.否则会导致每次都判断为false,导致result为空。

子集问题

排列问题

棋盘问题

迷宫问题
#include<bits/stdc++.h>
using namespace std;
int a, b;
vector<string> path;
vector<vector<string>> result;
void backtracking(vector<vector<int>>& data,int x,int y){
    int cur = data[x][y];
    int n =data[0].size();
    int n1 = data.size();
    if(cur!=0) return;
    if(cur==0&&x==(n1-1)&&y==(n-1)){
        path.push_back("(");
        path.push_back(to_string(x));
        path.push_back(",");
        path.push_back(to_string(y));
        path.push_back(")");
        result.push_back(path);
        return ;
    }
    if(cur==0){
        data[x][y]=1;
        path.push_back("(");
        path.push_back(to_string(x));
        path.push_back(",");
        path.push_back(to_string(y));
        path.push_back(")");
        if(x<n1-1)backtracking(data, x+1, y);
        if(y<n-1)backtracking(data, x, y+1); 
        if(x>0)backtracking(data, x-1, y); 
        if(y>0)backtracking(data, x, y-1); 

        path.pop_back();
        path.pop_back();
        path.pop_back();
        path.pop_back();
        path.pop_back();
    }
    
}
int main() {
    int tmp;
    cin>>a>>b;
    vector<vector<int>> data(a,vector<int>(b,1));
    for(int i=0;i<a;i++){
        for(int j=0;j<b;j++){
            cin>>tmp;
            data[i][j] = tmp;
        }
    }
    backtracking(data, 0, 0);
    for(int i =0;i<result.size();i++){
        int index=0;
        for(int j=0;j<result[i].size();j++){
            cout<<result[i][j];
            index++;
            if(index%5==0) cout<<endl;
        }
    }

    return 0;
}
// 64 位输出请用 printf("%lld")
这道题目中,由于每次都是前进一次,后退一次,所以四个方向判断,只需要回溯一次。
并且每走过一条路,将来路堵死,避免重复搜索

二叉树问题

494. 目标和

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。


class Solution {
public:
    int ans;
    int sum=0;
    void backtracking(vector<int>&nums,int target,int index){
        // if(sum>target) return;
        if(nums.size()==0&&target==0) return;
        if(index==nums.size()){
            if(sum==target) ans++;
            
            return;
        } 

            sum+=nums[index];
            backtracking(nums,target,index+1);
            sum-=nums[index];
            sum-=nums[index];
            backtracking(nums,target,index+1);
            sum+=nums[index];

    }
    int findTargetSumWays(vector<int>& nums, int target) {
        
        backtracking(nums,target,0);
        return ans;

    }
};
回溯不是所有情况都需要for循环,当第一层只需要选择一次时,可以不用for循环。然后在每次回溯的时候直接使用index+1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值