leetcode332.重新安排行程,C++做法

  • 思路:
    使用dfs回溯,主要解决两件事:
  1. 路径。(找到路径,并防止有环)
  2. 该路径上每次选择的节点都是字典序最小的(答案不唯一时)。
  • 解决问题1:如果要找一条路径,可以用组合的dfs模版,当path中存放可达地点字符串数量够机票张,则找到了一种情况。但是如何防止走到环中出不去。使用二维map,第二维记录当前地点可达下一位置的机票数量,在同层dfs处,如果发现机票数量为0,则跳过。这样就不会走入环。

解决问题2:如代码中使用二维map记录起始到目的地的机票数,第二维也使用了map,这样一来, 从src->dst的多个选择中,由于map是红黑树会自动排序,所以字典序小的字符串自动放在了靠前位置。递归中,每次从map中找可达位置,会优先选择字典序较小的目的地。
(关于上述两个点,代码随想录中写的很清楚,这里我只记录自己的一些思考点)。

  • 疑惑
  1. dfs什么时候带返回值,这个题不带会怎么样?
    不带返回值,最后path会因为遍历其它路径而更改掉,或者被pop_back(),path最后存的就不是正确答案。所以当答案唯一的dfs题,最好dfs带返回值,就不必执行多余的递归了。

  2. 如果我不带返回值,使用更高维度的res来存,且走完全部案例,返回res[0],会怎么样?
    会超时!试过了!

写法1:

#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<unordered_map>
#include<algorithm>

using namespace std;

// tar第二维一定优先字典序小的,第一个找到的就是
        // 如果回溯的值,path就是所求,可以给dfs加判断条件,或者另外存变量赋值即可。
        // 如果借用和path同维的变量,这个值还是会被后面的值改变,用一个更高维度的值,然后返回高维度值的第一个有元素即可

// 这个题会超过内存限制:所以还是得剪枝

class Solution {
public:
    // str:s1:可达+数量、s2:、s3:
    unordered_map<string, map<string, int>> tar;
    vector<vector<string>> res;
    vector<string> path;
    int nums;   //  票数
    bool dfs() {
        if (path.size() == nums+1) {
            res.push_back(path);
            return true;
        }
        // 当前
        string src = path.back();
        for (auto& kv: tar[src])
        {
            if (kv.second == 0)
                continue;
            kv.second--;
            path.push_back(kv.first);
            bool tmp = dfs();
            // 如果发现已经成了,就返回
            if (tmp)
                return true;
            kv.second++;
            path.pop_back();
        }
        // 末尾应该返回 false,因为如果中间部分不能返回true,则说明这条路径下来没找到,
        // 所以必须返回false,因为不可能dfs第一次选了正确的目标,不断给正确的走
        // 如果这里返回true,需要一路正确着下来,
        // 主要是这里的true,会影响到一些前面的错误情况
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        // 
        nums = tickets.size();
        for (int i = 0; i < tickets.size(); i++)
        {
            string src = tickets[i][0];
            string dst = tickets[i][1];
            if (tar.find(src) != tar.end()) {
                tar[src][dst]++;
            }
            else{
                tar[src][dst] = 1;
            }
        }
        path.push_back("JFK");
        dfs();
        return res.front();
    }
};

int main()
{
    Solution* s = new Solution();
    //vector<vector<string>> tickets = { {"JFK","SFO"},  {"JFK", "ATL"}, {"SFO","ATL"}, {"ATL","JFK"}, {"ATL","SFO"} };
    vector<vector<string>> tickets = { {"JFK","SFO"},  {"JFK", "ATL"}, {"SFO","ATL"}, {"ATL","JFK"}, {"ATL","SFO"} };

    s->findItinerary(tickets);

    return 0;
}

写法2:会超时,只有最后1组用例不通过。(其它思路)
将对票数的计数,转为全局的vis标记。vis[i]表示第i张票是否使用,且回溯过程中,对vis变量做回溯。

#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<algorithm>

using namespace std;

class Solution {
public:
    vector<string> path;
    vector<vector<string>> mp;
    // 模仿Swift中的写法:第二维用字符串数组
    int n;
    vector<bool> vis;
    bool dfs() {
        if (path.size() == n + 1) {
            return true;
        }
        for (int i = 0; i < n; i++)
        {
            string cur = path.back();
            /*cout << "当前是:" << cur << endl;
            cout << "查看的票是:" << mp[i][0] << endl;*/
            // 该票起始是现在,且票没用过才能出发,否则跳去下一张
            if (mp[i][0] != cur || vis[i]) {
                continue;
            }
            vis[i] = true;
            path.push_back(mp[i][1]);
            bool res = dfs();
            if (res) {
                return true;
            }
            path.pop_back();
            vis[i] = false;
        }
        return false;
    }

    static bool cmp(const vector<string>& s, const  vector<string>& t)
    {
        if (s[0] != t[0])
            return s[0] < t[0];
        else
            return s[1] < t[1];
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        n = tickets.size();
        vis.resize(n, false);   //  记录每张票是否使用
        // 直接对tickets的每一个票的终点的第二维排序,先获取每个点可达其点
        mp = tickets;
        sort(mp.begin(), mp.end(), cmp);
        path.push_back("JFK");
        dfs();
        return path;
    }
};

int main()
{
    Solution* s = new Solution();
    vector<vector<string>>tickets = { {"JFK","SFO"}, {"JFK","ATL"},{"SFO","ATL"}, {"ATL","JFK"}, {"ATL","SFO"}};
    s->findItinerary(tickets);

    return 0;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值