IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> LeetCode 力扣周赛 260 -> 正文阅读

[数据结构与算法]LeetCode 力扣周赛 260

[周赛传送门]https://leetcode-cn.com/contest/weekly-contest-260/

2016. 增量元素之间的最大差值

思路:暴力枚举

时间复杂度 O ( n 2 ) O(n^2) O(n2)

先说最直白的方法,枚举所有下标对 ( i , j ) (i,j) (i,j),判断 n u m s i nums_i numsi? n u m s j nums_j numsj? 的大小关系并更新答案。

class Solution {
public:
    int maximumDifference(vector<int>& nums) {
        int anw = -1; // 答案初始化为 -1
        for (int i = 0; i < nums.size(); i++) {
            for (int j = i+1; j < nums.size(); j++) {
                if (nums[i] < nums[j]) {
                	// 找到一对符合要求的下标,尝试更新答案
                    anw = max(anw, nums[j] - nums[i]);
                }
            }
        }
        return anw;
    }
};

思路:预处理最小值

时间复杂度 O ( n ) O(n) O(n)

设有长度为 n n n 的一维数组 o p t opt opt o p t i opt_i opti? 表示在前 i i i 个元素中的最小值。那么对于每个 n u m s i , i ≥ 1 nums_i,i \ge 1 numsi?i1,所能贡献的最大差值为 :

  • o p t i ? 1 ≤ n u m s i opt_{i-1} \le nums_i opti?1?numsi? 时,为 n u m s i ? o p t i ? 1 nums_i - opt_{i-1} numsi??opti?1?
  • 反之为 -1。

整体实现上,可以先 O ( n ) O(n) O(n) 的预处理 o p t opt opt。然后再 O ( n ) O(n) O(n) 的求出每个 n u m s i nums_i numsi? 的最大差值,其中最大的即为答案。

class Solution {
public:
    int maximumDifference(vector<int>& nums) {
        int anw = -1; 
        // 因为只关心 opt[i-1],所以 opt 可化简为一个变量。
        for (int i = 1, opt = nums[0]; i < nums.size(); i++) {
            if(opt < nums[i]) {
                anw = max(anw, nums[i] - opt);
            }
            opt = min(opt, nums[i]);
        }
        return anw;
    }
};

2017. 网格游戏

思路:贪心

时间复杂度 O ( n ) O(n) O(n)

不难发现,先手必定走过 n + 1 n+1 n+1 个格子。先手有 n n n 种走法,选择一个 位置 p p p p ∈ [ 0 , n ) p\in[0,n) p[0,n),先手会走过第一排的 [ 0 , p ] [0,p] [0p] ,以及第二排的 [ p , n ) [p, n) [p,n)

后手有以下两种方案:

  • ( 0 , 0 ) → ( 0 , n ? 1 ) → ( 1 , n ? 1 ) (0,0)→(0,n-1)→(1,n-1) (0,0)(0,n?1)(1,n?1),得分为第一排的 ( p , n ) (p,n) (p,n) 格子的和。
  • ( 0 , 0 ) → ( 1 , n ? 1 ) → ( 1 , n ? 1 ) (0,0)→(1,n-1)→(1,n-1) (0,0)(1,n?1)(1,n?1),得分为第二排的 [ 0 , p ) [0,p) [0,p) 格子的和。

不难得出先手的策略,选择一个 p p p,使得第一排 ( p , n ) (p,n) (p,n) 和 第二排 [ 0 , p ) [0,p) [0,p) 中的最大值最小。

class Solution {
public:
    long long gridGame(vector<vector<int>>& grid) {
        int64_t sum[2] = {0};
        int n = grid[0].size();
        for (int i = 0; i < n; i++) {
            sum[0] += grid[0][i]; // 计算第一排的累加和
            sum[1] += grid[1][i]; // 计算第二排的累加和
        }
        int64_t pre[100000] = {grid[0][0]};
        for (int i = 1; i < n; i++) {
            pre[i] = pre[i-1] + grid[0][i]; // 计算第一排的前缀和
        }
        // anw 为最终答案,初始化为一个极大值。
        int64_t anw = sum[0] + sum[1];
        // suf 为第二排的后缀和,简化为了一个变量
        int suf = 0;
        for (int i = n-1; i >= 0; i--) {
            suf += grid[1][i];// 计算后缀和
            // opt: 先手选择 p = i 时,后续的最优解.
            int opt = max(sum[0] - pre[i], sum[1] - suf);
            // anw 保存最小的「后手最优解」。
            anw = min(anw, opt);
        }
        return anw;
    }
};

2018. 判断单词是否能放入填字游戏内

思路 暴力枚举

时间复杂度 O ( ( n m ) 3 / 2 ) O((nm)^{3/2}) O((nm)3/2)

枚举坐标 ( i , j ) (i,j) (i,j),检查四个方向:

  • ( i , j ) → ( i + k ? 1 , j ) (i,j) → (i+k-1,j) (i,j)(i+k?1,j)
  • ( i , j ) → ( i ? k + 1 , j ) (i,j) → (i-k+1,j) (i,j)(i?k+1,j)
  • ( i , j ) → ( i , j + k ? 1 ) (i,j) → (i,j+k-1) (i,j)(i,j+k?1)
  • ( i , j ) → ( i , j ? k + 1 ) (i,j) → (i,j-k+1) (i,j)(i,j?k+1)

是否存在一个方向能放下 w o r d s words words,其中 k k k w o r d s words words 的长度。

另外,还需检查对应的边界是否符合要求。

整体上的时间复杂度为 O ( n m k ) O(nmk) O(nmk),考虑到 k k k的取值不超过 ( n , m ) \sqrt{(n,m)} (n,m) ? ,因此时间复杂度可简化为 O ( ( n m ) 3 / 2 ) O((nm)^{3/2}) O((nm)3/2)

class Solution {
  public:
    bool check(int x, int y, const vector<vector<char>> &board, int n, int m) {
        // (x,y) 在棋盘内部且为 #
        if (0 <= x && x < n && 0 <= y && y < m && board[x][y] == '#') {
            return true;
        }
        // 位于边界外层
        if (-1 == x || x == n || -1 == y || y == m) {
            return true;
        }
        return false;
    }
    bool placeWordInCrossword(vector<vector<char>>& board, string word) {
      int n = board.size(); // 行数
      int m = board[0].size(); // 列数
      int dx[4] = {-1, 1, 0, 0}; // 定义方向数组
      int dy[4] = {0 , 0, 1,-1};

      // 两层for循环枚举起始点 (i,j)
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
          // 枚举方向
          for (int d = 0; d < 4; d++) {
            int sx = i - dx[d];
            int sy = j - dy[d];
            // 检查端点 (sx,sy);
            if (!check(sx, sy, board, n, m)) {
                continue;
            }
            
            int ex = i + (word.size())*dx[d];
            int ey = j + (word.size())*dy[d];
            // 检查端点 (ex,ey);
            if (!check(ex, ey, board, n, m)) {
                continue;
            }

            // 两端点满足要求,继续检查其他位置。
            bool flag = true;
            for (int k = 0, x = i, y = j; k < word.size() && flag; k++, x += dx[d], y += dy[d]) {
              if (!(board[x][y] == ' ' || board[x][y] == word[k])) {
                  flag = false;
              }
            }
            if (flag) { return flag; }
          }
        }
      }
      return false;
    }
};

2019. 解出数学表达式的学生分数

思路:动态规划

时间复杂度 O ( n 2 ? m 2 + k ) O(n^2*m^2 + k) O(n2?m2+k) n n n 为 字符串的长度。 m m m 为取值上限,本题为 1000。 k k k 为提交的答案数。

计算正确值的思路比较简单,可以借助栈来实现先乘后加。

    int getCorrect(const std::string &s) {
        stack<int> st;
        // 把第一个数字先放进去。
        st.push(s[0]-'0');

        // 枚举运算符号
        for (int i = 1; i < s.size(); i += 2) {
            if (s[i] == '*') {
                // 是乘号,下一数字与栈顶元素相乘
                st.top() *= s[i+1]-'0';
            } else {
                // 是加号,下一数字放入栈顶
                st.push(s[i+1]-'0');
            }
        }
        int sum = 0;
        // 栈中的数字需要求累加和。
        while (!st.empty()) {
            sum += st.top();
            st.pop();
        }
        return sum;
    }

乱序执行的结果,可用动态规划求解。设有二维数组 d p dp dp d p l , r dp_{l,r} dpl,r? 是一个集合,表示表达式 s l , r s_{l,r} sl,r? 乱序执行结果的集合。

通过枚举运算符将表达式一分为二,比如有 s l , r s_{l,r} sl,r?,以及运算符 s p s_p sp?, 则将表达式分为 s l , p ? 1 s_{l,p-1} sl,p?1? 以及 s p + 1 , r s_{p+1,r} sp+1,r?

不难想到状态转移方程:

  • l = r l = r l=r 时:
    d p l , r = s l dp_{l,r} = s_l dpl,r?=sl?
  • l ≠ r l\ne r l?=r时:
    d p l , r = x ? y , x ∈ d p l , p ? 1 , y ∈ d p p + 1 , r dp_{l,r} = x · y, x\in dp_{l,p-1}, y\in dp_{p+1,r} dpl,r?=x?y,xdpl,p?1?,ydpp+1,r?

因此,可在 O ( n 2 ? m 2 ) O(n^2*m^2) O(n2?m2) 时间复杂度内求出所有的 d p l , r dp_{l,r} dpl,r? n n n 为 字符串的长度。 m m m 为取值上限,本题为 1000。

最后,枚举提交的答案,求解总分数即可。

class Solution {
public:
    int getCorrect(const std::string &s) {
        stack<int> st;
        // 把第一个数字先放进去。
        st.push(s[0]-'0');

        // 枚举运算符号
        for (int i = 1; i < s.size(); i += 2) {
            if (s[i] == '*') {
                // 是乘号,下一数字与栈顶元素相乘
                st.top() *= s[i+1]-'0';
            } else {
                // 是加号,下一数字放入栈顶
                st.push(s[i+1]-'0');
            }
        }
        int sum = 0;
        // 栈中的数字需要求累加和。
        while (!st.empty()) {
            sum += st.top();
            st.pop();
        }
        return sum;
    }

    // dp[l][r] 表示子表达式 s[l:r] 可能出现的值的集合
    unordered_set<int> dp[32][32];

    void getPossible(const std::string &s, int l, int r) {
        if (dp[l][r].size()) {
            // 已经计算过了,没必要重复计算了。直接返回
            return;
        }

        if (l == r) {
            // 长度为 1 的子表达式,必然包含一个数字。
            dp[l][r].insert(s[l]-'0');
            return;
        }

        // 枚举运算符号,分割为两个子表达式。
        for (int i = l+1; i < r; i += 2) {
            getPossible(s, l, i-1); // 计算子表达式 s[l,i-1]
            getPossible(s, i+1, r); // 计算子表达式 s[i+1,r]
            
            // 由两个子表达式的集合计算得出 s[l,r] 的值。
            for (auto vl : dp[l][i-1]) {
                for (auto vr : dp[i+1][r]) {
                    auto v = ((s[i] == '+') ? (vl + vr) : (vl * vr));
                    if (v <= 1000) {
                        dp[l][r].insert(v);
                    }
                }
            }
        }
    }

    int scoreOfStudents(string s, vector<int>& answers) {
        // 先计算正确值
        int correct = getCorrect(s);

        // 计算优先级出错可能得出的值
        int n = s.size()-1;
        getPossible(s, 0, n);

        // 累加总分
        int anw = 0;
        for (auto a : answers) {
            if (a == correct) {
                anw +=5;
            } else if(dp[0][n].count(a) == 1) {
                anw += 2;
            }
        }
        return anw;
    }
};
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-01 17:08:43  更:2021-10-01 17:10:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 12:11:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码