LeetCode 随机刷题2

1575. 统计所有可行路径

难度:困难。
标签:动态规划。

这个题我写了三个小时,然后超时了。哭了。

思路是这样的:
首先,肯定是动态规划,开始想到使用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示从i到j花费燃料小于等于k的路径数,想了想试了试发现不行,若加上小于k的路径数,这样会重复计算很多路径,因此用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示从i到j花费燃料恰好等于k的路径数。
主要思想就是分割,将i->j的路径分割成i->k和j->k,k是任何一个i在使用小于等于fuel可以到达的地方,其中有一种情况不包括,即i使用0燃料到达i,即k不等于i,可以等于j。
为了防止重复计算,每次规定i->k是直达的,即i->k花费燃料数为 f 1 = a b s ( l o c a t i o n s [ i ] − l o c a t i o n s [ k ] ) f1 = abs(locations[i] - locations[k]) f1=abs(locations[i]locations[k]),这种情况只有1种。k->j可以有多种方案,即 d p [ k ] [ j ] [ f u e l − f 1 ] dp[k][j][fuel - f1] dp[k][j][fuelf1],其中可以k = j。

超时代码:

class Solution {
    int max_value = pow(10, 9) + 7;

public:
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        int n = locations.size();
        int startPos = locations[start];
        int finishPos = locations[finish];
        sort(locations.begin(), locations.end());

        vector<vector<vector<int>>> dp(n, vector<vector<int>>(n, vector<int>(fuel + 1)));
        for(int f = 1; f <= fuel; f++){
            for(int step = 0; step < n; step++){
                for(int i = 0; i < n - step; i++){
                    int j = i + step;
                    int min_fuel = abs(locations[i] - locations[j]);
                    if(min_fuel > f){
                        continue;
                    }
                    else if(min_fuel == f){
                        dp[i][j][f] = 1;
                        dp[j][i][f] = 1;
                    }
                    int begin = max(i - f, 0);
                    int end = min(i + f, n - 1);
                    for(int k = begin; k <= end; k++){
                        int use_f = abs(locations[k] - locations[i]);
                        if(use_f > f || use_f == 0)continue;
                        dp[i][j][f] += dp[k][j][f - use_f];
                        if(dp[i][j][f] > max_value)dp[i][j][f] %= max_value;
                    }
                    dp[j][i][f] = dp[i][j][f];
                }
            }
        }

        int a = -1, b = -1;
        for(int i = 0; i < n; i++){
            if(a != -1 && b != -1)break;
            if(locations[i] == startPos)a = i;
            if(locations[i] == finishPos)b = i;
        }
        long long result = 0;
        for(int i = 1; i <= fuel; i++){
            result += dp[a][b][i];
            if(result > max_value)result %= max_value;
        }

        if(a == b)return result + 1;
        return result;
    }
};

优化一下,根据fuel,i,j求出k的范围。继续超时。

超时代码:

class Solution {
    int max_value = pow(10, 9) + 7;

public:
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        int n = locations.size();
        int startPos = locations[start];
        int finishPos = locations[finish];
        if(abs(startPos - finishPos) > fuel)return 0;
        sort(locations.begin(), locations.end());

        vector<vector<vector<int>>> dp(n, vector<vector<int>>(n, vector<int>(fuel + 1)));
        for(int f = 1; f <= fuel; f++){
            for(int step = 0; step < n; step++){
                for(int i = 0; i < n - step; i++){
                    int j = i + step;
                    int min_fuel = abs(locations[i] - locations[j]);
                    if(min_fuel > f){
                        continue;
                    }
                    else if(min_fuel == f){
                        dp[i][j][f] = 1;
                        dp[j][i][f] = 1;
                    }
                    int begin = (locations[i] + locations[j] - f) / 2 - 1;
                    int end = (f + locations[i] + locations[j]) / 2 + 1;
                    int idx_begin = max(i - (locations[i] - begin) - 1, 0);
                    int idx_end = min(i + (end - locations[i]) + 1, n - 1);
                    for(int k = idx_begin; k <= idx_end; k++){
                        if(locations[k] < begin || locations[k] > end)continue;
                        int use_f = abs(locations[k] - locations[i]);
                        if(use_f > f || use_f == 0)continue;
                        dp[i][j][f] += dp[k][j][f - use_f];
                        if(dp[i][j][f] > max_value)dp[i][j][f] %= max_value;
                    }
                    dp[j][i][f] = dp[i][j][f];
                }
            }
        }
        
        vector<int>::iterator it;
        it = find(locations.begin(), locations.end(), startPos);
        int a = it - locations.begin();
        int b = a;
        if(startPos != finishPos){
            it = find(locations.begin(), locations.end(), finishPos);
            b = it - locations.begin();
        }

        long long result = 0;
        for(int i = 1; i <= fuel; i++){
            result += dp[a][b][i];
            if(result > max_value)result %= max_value;
        }

        if(a == b)return result + 1;
        return result;
    }
};

好吧,我承认我失败了。看题解吧。这个思想不是最优的吧。
由于start是固定的,所以第一维是不需要的,使用 d p [ j ] [ k ] dp[j][k] dp[j][k]表示兄start到j恰好使用燃料k的路径总数。
我吐了,就这么简单。

正确解法:

class Solution {
    int max_value = pow(10, 9) + 7;

public:
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        int n = locations.size();
        if(abs(locations[start] - locations[finish]) > fuel)return 0;

        vector<vector<int>> dp(n, vector<int>(fuel + 1));
        dp[start][0] = 1;
        for(int f = 1; f <= fuel; f++){
            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    if(i == j)continue;
                    int use_f = abs(locations[i] - locations[j]);
                    if(f - use_f < 0)continue;
                    dp[j][f] += dp[i][f - use_f];
                    if(dp[j][f] > max_value)dp[j][f] %= max_value;
                }
            }
        }
        
        long result = 0;
        for(int i = 0; i <= fuel; i++){
            result += dp[finish][i];
            if(result > max_value)result %= max_value;
        }
        return result;
    }
};

结果:
在这里插入图片描述

总结:思考动态规划问题,往往想到的可能都是复杂版本,比如上述那个三维的,在开始写之前,想一想给定的条件,比如从start开始出发,根据给定条件可以简化方法,理清思路。

PS:要改掉试出答案的坏习惯,这样太浪费时间了,要先好好思考各种情况,得出较完整的思路后再继续。

面试题 17.13. 恢复空格

开始看错了题,看成了返回未匹配的单词个数,而不是字符个数。所以导致整个理解都有偏差。
其实应该是一道比较简单的动态规划题,加上哈希表来匹配单词即可。但这样做效率很低。

class Solution {
public:
    int respace(vector<string>& dictionary, string sentence) {
        int word_num = dictionary.size();
        int len = sentence.length();
        if(word_num == 0)return len;

        unordered_set<string> dict;
        for(int i = 0; i < word_num; i++){
            if(dict.find(dictionary[i]) == dict.end()){
                dict.insert(dictionary[i]);
            }
        }
        vector<int> dp(len + 1);
        for(int i = 1; i <= len; i++){
            dp[i] = dp[i - 1] + 1;
            for(int k = 0; k < i; k++){
                string s = sentence.substr(k, i - k);
                if(dict.find(s) != dict.end())dp[i] = min(dp[i], dp[k]);
            }
        }
        
        return dp[len];
    }
};

结果:
在这里插入图片描述

可以使用字典树进行优化。学习一下字典树!根据动态规划原理,这个题适合后序生成字典树。

正确解法:

class TrieNode{
public:
    bool isWord;
    TrieNode* children[26] = {nullptr};
    TrieNode(){
        isWord = false;
    }
};

class Trie{
public:
    TrieNode* root;

    Trie(){
        root = new TrieNode();
    }    

    void insert(string word){
        TrieNode* cur = root;
        for(int i = word.length() - 1; i >= 0; i--){
            int c = word[i] - 'a';
            if(cur->children[c] == NULL){
                cur->children[c] = new TrieNode();
            }
            cur = cur->children[c];
        }
        cur->isWord = true;
    }


    vector<int> search(string sentence, int endPos){
        vector<int> indices;
        TrieNode* cur = root;
        for(int i = endPos; i >= 0; i--){
            int c = sentence[i] - 'a';
            if(cur->children[c] == NULL)break;
            cur = cur->children[c];
            if(cur->isWord)indices.push_back(i);
        }
        return indices;
    }
};

class Solution {
public:
    int respace(vector<string>& dictionary, string sentence) {
        int word_num = dictionary.size();
        int len = sentence.length();
        if(word_num == 0)return len;
        Trie* tree = new Trie();
        for(int i = 0; i < word_num; i++){
            tree->insert(dictionary[i]);
        }

        vector<int> dp(len + 1);
        for(int i = 1; i <= len; i++){
            dp[i] = dp[i - 1] + 1;
            vector<int> res = tree->search(sentence, i - 1);
            for(int k = 0; k < res.size(); k++){
                dp[i] = min(dp[i], dp[res[k]]);
            }
        }
        return dp[len];
    }
};

结果:
在这里插入图片描述

面试题 08.08. 有重复字符串的排列组合

正确解法:

class Solution {
    vector<string> result;

    void dfs(string s, string& a, vector<int>& visited){
        if(a.length() == s.length()){
            result.emplace_back(a);
            return;
        }

        int last_index = -1;
        for(int i = 0; i < s.length(); i++){
            if(visited[i] == 0){
                if(i > 0 && last_index != -1 && s[i] == s[last_index])continue;
                a += s[i];
                visited[i] = 1;
                dfs(s, a, visited);
                a.pop_back();
                visited[i] = 0;
                last_index = i;
            }
        }
    }

public:
    vector<string> permutation(string s) {
        sort(s.begin(), s.end());

        string a = "";
        vector<int> visited(s.length());
        dfs(s, a, visited);
        return result;
    }
};

结果:
在这里插入图片描述

1363. 形成三的最大倍数

难度:困难。
标签:数学,动态规划。

这个题很好想,但是确实有坑。不使用动态规划,使用贪心的思想。
结果是3的倍数,个个数字的和是3的倍数。
先对序列从大到小排序,使用一个矩阵把每个数字mod3的结果存储下来。

  • 若a % 3 == 0,则直接加入最终结果中
  • 将3个对3取余结果是1的数加入结果
  • 将3个对3取余结果是2的数加入结果
  • 将1个对3取余结果是1和1个对3取余结果是2的数加入结果

因此,只需要算出最终要加入结果的取余结果是1的数 m o d 1 N u m mod1Num mod1Num的个数和取余结果是2的数的个数 m o d 2 N u m mod2Num mod2Num即可。按照序列从大到小的顺序取数字。

其中对 m o d 1 N u m mod1Num mod1Num的计算和 m o d 2 N u m mod2Num mod2Num的计算需要稍微多思考一下,对于 m o d 1 N u m > m o d 2 N u m mod1Num > mod2Num mod1Num>mod2Num来说,有以下几种情况(反之同理):

  • 若mod2Num==0,则mod1Num -= mod1Num % 3
  • 若(mod1Num - mod2Num) % 3 == 2,则mod2Num -= 1
  • 其余情况mod1_num -= (mod1_num - mod2_num) % 3

以下是易错的测试用例:

[1,1,1,2]
[5,8]
[1,4,0,0,0,0]

正确解法:

class Solution {

    static bool cmp(int& a, int& b){
        return a > b;
    }

public:
    string largestMultipleOfThree(vector<int>& digits) {
        string result = "";
        int n = digits.size();
        sort(digits.begin(), digits.end(), cmp);
        if(digits[0] == 0){
            result = "0";
            return result; 
        }

        vector<int> mod_vec(n);
        int mod2_num = 0, mod1_num = 0;
        for(int i = 0; i < n; i++){
            int x = digits[i] % 3;
            mod_vec[i] = x;
            if(x == 2)mod2_num++;
            else if(x == 1)mod1_num++;
        }
        if(mod1_num < mod2_num){
            if((mod2_num - mod1_num) % 3 == 2 && mod1_num > 0){
                mod1_num -= 1;
            }
            else{
                mod2_num -= (mod2_num - mod1_num) % 3;
            }
        }
        else if(mod1_num > mod2_num){
            if((mod1_num - mod2_num) % 3 == 2 && mod2_num > 0){
                mod2_num -= 1;
            }
            else{
                mod1_num -= (mod1_num - mod2_num) % 3;
            }
        }

        for(int i = 0; i < n; i++){
            if(mod_vec[i] == 0){
                result += char('0' + digits[i]);
            }
            else if(mod_vec[i] == 1 && mod1_num){
                result += char('0' + digits[i]);
                mod1_num--;
            }
            else if(mod_vec[i] == 2 && mod2_num){
                result += char('0' + digits[i]);
                mod2_num--;
            }
        }
        if(result[0] == '0')return "0";
        return result;
    }
};

结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值