第 132 场周赛

题目列表:


5024. 除数博弈:

爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。

最初,黑板上有一个数字 N 。在每个玩家的回合,玩家需要执行以下操作:

  • 选出任一 x,满足 0 < x < N 且 N % x == 0 。
  • 用 N - x 替换黑板上的数字 N 。

如果玩家无法执行这些操作,就会输掉游戏。

只有在爱丽丝在游戏中取得胜利时才返回 True,否则返回 false。假设两个玩家都以最佳状态参与游戏。

示例 1: 输入:2 输出:true 解释:爱丽丝选择 1,鲍勃无法进行操作。

示例 2: 输入:3 输出:false 解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。

解法一:

分析:

1 的时候爱丽丝必输

2 的时候爱丽丝选择1, 让鲍勃陷入必输的状态, 此状态为必赢状态。

3 的时候爱丽丝 只能选择1, 让鲍勃陷入必赢的状态

4 时候爱丽丝选择2, 让鲍勃选择2, 此状态为必赢状态, 依次类推

假设 a1, a2, a3 ......., an 是的在[1, N) 之间能使N被整除的数字, 那么

dp[i] 值为 bool 表示面临 i 的时候的输赢状况,

则状态转移方程为:

dp[N] = !dp[N - a1] || !dp[a2] || ........ || !dp[an];

class Solution 
{ 
public: 
    bool divisorGame(int N) 
    { 
       vector<bool> dp(N + 1); 
       dp[1] = false; for(int i = 2; i <= N; ++i) 
       { 
           dp[i] = false; 
           for(int k = 1; k != i; ++k) 
           { 
               if(i % k == 0 && !dp[i]) dp[i] = dp[i] || !dp[i - k]; 
           } 
       } return dp[N]; 
    } 
};

解法二:

牛逼克拉斯的解法, 不晓得怎么证明:

class Solution 
{ 
public: 
    bool divisorGame(int N) 
    { 
        return (N % 2 == 0); 
    } 
};

5030. 节点与其祖先之间的最大差值

给定二叉树的根节点 root,找出存在于不同节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。

(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)

示例:

输入:[8,3,10,1,6,null,14,null,null,4,7,13] 
输出:7 
解释: 我们有大量的节点与其祖先的差值,其中一些如下: |8 - 3| = 5 |3 - 7| = 4 |8 - 1| = 7 |10 - 13| = 3 在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出。

提示:

  1. 树中的节点数在 2 到 5000 之间。
  2. 每个节点的值介于 0 到 100000 之间。

解题思路:

使用先序遍历的方式, 以每个结点为根节点来与其所有子孩子之间进行计算差值, 在此过程当中来计算出最大值是多少,考虑到结点数量可能有点多, 则我们使用迭代的方式来实现先序遍历:

#include <cmath>
class Solution {
public:
    int max_val;
    int maxAncestorDiff(TreeNode* root) 
    {
        max_val = INT_MIN;
        preorder(root);
        return max_val;
    }
    
    //先序遍历
    void preorder(TreeNode *root)
    {
        stack<TreeNode *> nodes;
        
        if(!root) return;
        nodes.push(root);
        
        while(!nodes.empty())
        {
            auto node = nodes.top(); nodes.pop();
            
            //直接对其左边的孩子进行计算
            while(node)
            {
                caclute(node);
                if(node->right) nodes.push(node->right);
                node = node->left;    
            }
        }
    }
    
    //计算当前结点的值 和 其所有孩子结点的值之间的差值
    void caclute(TreeNode *root)
    {
        stack<TreeNode *> nodes;
        
        if(!root) return;
        nodes.push(root);
        
        while(!nodes.empty())
        {
            auto node = nodes.top(); nodes.pop();
            
            //直接对其左边的孩子进行计算
            while(node)
            {
                max_val = max(max_val, abs(root->val - node->val));
                if(node->right) nodes.push(node->right);
                node = node->left;    
            }
        } 
    }
};

5025. 最长等差数列

给定一个整数数组 A,返回 A 中最长等差子序列的长度。

回想一下,A 的子序列是列表 A[i_1], A[i_2], ..., A[i_k] 其中 0 <= i_1 < i_2 < ... < i_k <= A.length - 1。并且如果 B[i+1] - B[i]( 0 <= i < B.length - 1) 的值都相同,那么序列 B 是等差的。

示例 1: 输入:[3,6,9,12] 输出:4 解释: 整个数组是公差为 3 的等差数列。 示例 2: 输入:[9,4,7,2,10] 输出:3 解释: 最长的等差子序列是 [4,7,10]。 示例 3: 输入:[20,1,15,3,10,5,8] 输出:4 解释: 最长的等差子序列是 [20,15,10,5]。

提示:

2 <= A.length <= 2000
0 <= A[i] <= 10000

解法一:

    暴力破解。 双重for循环得到顶点对 i , j, 并在 j + 1 , n 之间在寻找以i 和 j 开始等差数列元素即可.

解法二:

    动态规划。

    以i结尾, 并且 d = i - j 为等差值的序列的长度:

    dp[i - j, i] = 2;

    dp[i - j , j] = max(dp[i - j, j] + 1, dp[i - j, i]);

class Solution 
{
public:
    int longestArithSeqLength(vector<int>& A) 
    {
        map<pair<int, int>, int> dp;
        int res = 2;
        for(int i = 1; i < A.size(); ++i)
        {
            for(int j = 0; j < i; ++j)
            {
                dp[{A[i] - A[j], i}] = 2;
                dp[{A[i] - A[j], i}] = max(dp[{A[i] - A[j], j}] + 1, dp[{A[i] - A[j], i}]);
                res = max(dp[{A[i] - A[j], i}], res);
            }
        }
    
        return res;
    }
};

5031. 从先序遍历还原二叉树

我们从二叉树的根节点 root 开始进行深度优先搜索。

在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。

如果节点只有一个子节点,那么保证该子节点为左子节点。

给出遍历输出 S,还原树并返回其根节点 root。

示例 1:
输入:"1-2--3--4-5--6--7" 输出:[1,2,5,3,4,6,7]

示例 2:

输入:"1-2--3---4-5--6---7" 输出:[1,2,5,3,null,6,null,4,null,7]

示例 3:

输入:"1-401--349---90--88" 输出:[1,401,null,349,88,90]

提示:

  • 原始树中的节点数介于 1 和 1000 之间。
  • 每个节点的值介于 1 和 10 ^ 9 之间。

解法:

使用先序遍历的方式, 并标记当前结点的层次, 和递归过程中 标记的 ‘’-‘’的数量 来判断是否构造。

class Solution 
{
public:
    TreeNode* recoverFromPreorder(string S) 
    {
        int cur = 0, pre_cnt = 0;
        return build(S, cur, pre_cnt, 0);
    }
    
    TreeNode *build(string &s, int &cur, int &cnt, int level)
    {
        //当层级别 和 读取 - 数量相等的时候代表能使用此值来进行构建
        TreeNode *node = NULL;
        if(level == cnt && cur < s.size())
        {
            //取值, 假设cur定位在数字上面
            int val = 0;
            for(;cur < s.size() && s[cur] != '-'; ++cur)
            {
                val = val * 10 + (s[cur] - '0');
            }
            node = new TreeNode(val);
            cnt = 0;
            for(;cur < s.size() && s[cur] == '-'; ++cur)
                ++cnt;
            
            node->left  = build(s, cur, cnt, level + 1);
            node->right = build(s, cur, cnt, level + 1);
        }
        return node;
    }
};

发布了17 篇原创文章 · 获赞 23 · 访问量 3420

猜你喜欢

转载自blog.csdn.net/DO_HACKER/article/details/89341361