Skip to content

2596. 检查骑士巡视方案(medium)

2023/9/13

00 : 20 : 58


题目描述

骑士在一张 n x n 的棋盘上巡视。在有效的巡视方案中,骑士会从棋盘的 左上角 出发,并且访问棋盘上的每个格子 恰好一次

给你一个 n x n 的整数矩阵 grid ,由范围 [0, n * n - 1] 内的不同整数组成,其中 grid[row][col] 表示单元格 (row, col) 是骑士访问的第 grid[row][col] 个单元格。骑士的行动是从下标 0 开始的。

如果 grid 表示了骑士的有效巡视方案,返回 true;否则返回 false

注意,骑士行动时可以垂直移动两个格子且水平移动一个格子,或水平移动两个格子且垂直移动一个格子。下图展示了骑士从某个格子出发可能的八种行动路线。 img

示例 1:

img

输入:grid = [[0,11,16,5,20],[17,4,19,10,15],[12,1,8,21,6],[3,18,23,14,9],[24,13,2,7,22]]
输出:true
解释:grid 如上图所示,可以证明这是一个有效的巡视方案。

示例 2:

img

输入:grid = [[0,3,6],[5,8,1],[2,7,4]]
输出:false
解释:grid 如上图所示,考虑到骑士第 7 次行动后的位置,第 8 次行动是无效的。

提示:

  • n == grid.length == grid[i].length
  • 3 <= n <= 7
  • 0 <= grid[row][col] < n * n
  • grid 中的所有整数 互不相同

我的答案

class Solution {
public:
    bool check(vector<vector<int>>& grid, int& posX, int& posY, int& x)
    {
        int n = grid.size();
        // 1
        if (posX > 1 && posY > 0 && grid[posX-2][posY-1] == x+1)
        {
            x++;
            posX -= 2;
            posY -= 1;
            return true;
        }
        // 2
        if (posX > 0 && posY > 1 && grid[posX-1][posY-2] == x+1)
        {
            x++;
            posX -= 1;
            posY -= 2;
            return true;
        }
        // 3
        if (posX < n-1 && posY > 1 && grid[posX+1][posY-2] == x+1)
        {
            x++;
            posX += 1;
            posY -= 2;
            return true;
        }
        // 4
        if (posX < n-2 && posY > 0 && grid[posX+2][posY-1] == x+1)
        {
            x++;
            posX += 2;
            posY -= 1;
            return true;
        }
        // 5
        if (posX > 1 && posY < n-1 && grid[posX-2][posY+1] == x+1)
        {
            x++;
            posX -= 2;
            posY += 1;
            return true;
        }
        // 6
        if (posX > 0 && posY < n-2 && grid[posX-1][posY+2] == x+1)
        {
            x++;
            posX -= 1;
            posY += 2;
            return true;
        }
        // 7
        if (posX < n-1 && posY < n-2 && grid[posX+1][posY+2] == x+1)
        {
            x++;
            posX += 1;
            posY += 2;
            return true;
        }
        // 8
        if (posX < n-2 && posY < n-1 && grid[posX+2][posY+1] == x+1)
        {
            x++;
            posX += 2;
            posY += 1;
            return true;
        }
        return false;

    }
    bool checkValidGrid(vector<vector<int>>& grid) {
        int x = 0;
        int n = grid.size();
        int posX = 0, posY = 0;
        if (grid[0][0] != 0)
        {
            return false;
        }
        while (x < n*n - 1)
        {
            if (!check(grid, posX, posY, x))
            {
                return false;
            }
        }
        return true;
    }
};

参考答案

class Solution {
public:
    bool checkValidGrid(vector<vector<int>>& grid) {
        if (grid[0][0] != 0) {
            return false;
        }
        int n = grid.size();
        vector<array<int, 2>> indices(n * n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                indices[grid[i][j]] = {i, j};
            }
        }
        for (int i = 1; i < indices.size(); i++) {
            int dx = abs(indices[i][0] - indices[i - 1][0]);
            int dy = abs(indices[i][1] - indices[i - 1][1]);
            if (dx * dy != 2) {
                return false;
            }
        }
        return true;
    }
};

感想

可以预先存放indices数组为骑士依次走过的位置, 再进行一个绝对值的乘积的判断即可

1222. 可以攻击国王的皇后(medium)

2023/9/14

00 : 16 : 58


题目描述

在一个 8x8 的棋盘上,放置着若干「黑皇后」和一个「白国王」。

给定一个由整数坐标组成的数组 queens ,表示黑皇后的位置;以及一对坐标 king ,表示白国王的位置,返回所有可以攻击国王的皇后的坐标(任意顺序)。

示例 1:

img

输入:queens = [[0,1],[1,0],[4,0],[0,4],[3,3],[2,4]], king = [0,0]
输出:[[0,1],[1,0],[3,3]]
解释: 
[0,1] 的皇后可以攻击到国王,因为他们在同一行上。 
[1,0] 的皇后可以攻击到国王,因为他们在同一列上。 
[3,3] 的皇后可以攻击到国王,因为他们在同一条对角线上。 
[0,4] 的皇后无法攻击到国王,因为她被位于 [0,1] 的皇后挡住了。 
[4,0] 的皇后无法攻击到国王,因为她被位于 [1,0] 的皇后挡住了。 
[2,4] 的皇后无法攻击到国王,因为她和国王不在同一行/列/对角线上。

示例 2:

img

输入:queens = [[0,0],[1,1],[2,2],[3,4],[3,5],[4,4],[4,5]], king = [3,3]
输出:[[2,2],[3,4],[4,4]]

示例 3:

img

输入:queens = [[5,6],[7,7],[2,1],[0,7],[1,6],[5,1],[3,7],[0,3],[4,0],[1,2],[6,3],[5,0],[0,4],[2,2],[1,1],[6,4],[5,4],[0,0],[2,6],[4,5],[5,2],[1,4],[7,5],[2,3],[0,5],[4,2],[1,0],[2,7],[0,1],[4,6],[6,1],[0,6],[4,3],[1,7]], king = [3,4]
输出:[[2,3],[1,4],[1,6],[3,7],[4,3],[5,4],[4,5]]

提示:

  • 1 <= queens.length <= 63
  • queens[i].length == 2
  • 0 <= queens[i][j] < 8
  • king.length == 2
  • 0 <= king[0], king[1] < 8
  • 一个棋盘格上最多只能放置一枚棋子。

我的答案

class Solution {
public:
    int distance(vector<int> queen, vector<int> king)
    {
        int differX = queen[0] - king[0];
        int differY = queen[1] - king[1];
        return differX * differX + differY * differY;
    }
    vector<vector<int>> queensAttacktheKing(vector<vector<int>>& queens, vector<int>& king) {
        vector<vector<int>> res;
        // 0 1 2
        // 3   4
        // 5 6 7
        unordered_map<int, vector<int>> canAtt;
        for (auto & queen : queens)
        {
            int differX = queen[0] - king[0];
            int differY = queen[1] - king[1];
            int flag;
            if (differX == 0)
            {
                flag = differY > 0 ? 4 : 3;
            }
            else if (differY == 0)
            {
                flag = differX > 0 ? 6 : 1;
            }
            else if (differX == differY)
            {
                flag = differX > 0 ? 7 : 0;
            }
            else if (differX == -differY)
            {
                flag = differX > 0 ? 5 : 2;
            }
            else
            {
                continue;
            }
            if (canAtt.find(flag) == canAtt.end())
            {
                canAtt[flag] = queen;
            }
            else if (distance(queen, king) < distance(canAtt[flag], king))
            {
                canAtt[flag] = queen;
            }
        }
        for (auto & it : canAtt)
        {
            res.push_back(it.second);
        }
        return res;
    }
};

参考答案

class Solution {
public:
    vector<vector<int>> queensAttacktheKing(vector<vector<int>>& queens, vector<int>& king) {
        unordered_set<int> queen_pos;
        for (const auto& queen: queens) {
            int x = queen[0], y = queen[1];
            queen_pos.insert(x * 8 + y);
        }

        vector<vector<int>> ans;
        for (int dx = -1; dx <= 1; ++dx) {
            for (int dy = -1; dy <= 1; ++dy) {
                if (dx == 0 && dy == 0) {
                    continue;
                }
                int kx = king[0] + dx, ky = king[1] + dy;
                while (kx >= 0 && kx < 8 && ky >= 0 && ky < 8) {
                    int pos = kx * 8 + ky;
                    if (queen_pos.count(pos)) {
                        ans.push_back({kx, ky});
                        break;
                    }
                    kx += dx;
                    ky += dy;
                }
            }
        }
        return ans;
    }
};

感想

从国王出发, 可以维护一个哈希表

LCP 50. 宝石补给(easy)

2023/9/15

00 : 02 : 31


题目描述

欢迎各位勇者来到力扣新手村,在开始试炼之前,请各位勇者先进行「宝石补给」。

每位勇者初始都拥有一些能量宝石, gem[i] 表示第 i 位勇者的宝石数量。现在这些勇者们进行了一系列的赠送,operations[j] = [x, y] 表示在第 j 次的赠送中 第 x 位勇者将自己一半的宝石(需向下取整)赠送给第 y 位勇者。

在完成所有的赠送后,请找到拥有最多宝石的勇者和拥有最少宝石的勇者,并返回他们二者的宝石数量之差

注意:

  • 赠送将按顺序逐步进行。

示例 1:

输入:gem = [3,1,2], operations = [[0,2],[2,1],[2,0]]

输出:2

解释: 第 1 次操作,勇者 0 将一半的宝石赠送给勇者 2gem = [2,1,3] 第 2 次操作,勇者 2 将一半的宝石赠送给勇者 1gem = [2,2,2] 第 3 次操作,勇者 2 将一半的宝石赠送给勇者 0gem = [3,2,1] 返回 3 - 1 = 2

示例 2:

输入:gem = [100,0,50,100], operations = [[0,2],[0,1],[3,0],[3,0]]

输出:75

解释: 第 1 次操作,勇者 0 将一半的宝石赠送给勇者 2gem = [50,0,100,100] 第 2 次操作,勇者 0 将一半的宝石赠送给勇者 1gem = [25,25,100,100] 第 3 次操作,勇者 3 将一半的宝石赠送给勇者 0gem = [75,25,100,50] 第 4 次操作,勇者 3 将一半的宝石赠送给勇者 0gem = [100,25,100,25] 返回 100 - 25 = 75

示例 3:

输入:gem = [0,0,0,0], operations = [[1,2],[3,1],[1,2]]

输出:0

提示:

  • 2 <= gem.length <= 10^3
  • 0 <= gem[i] <= 10^3
  • 0 <= operations.length <= 10^4
  • operations[i].length == 2
  • 0 <= operations[i][0], operations[i][1] < gem.length

我的答案

class Solution {
public:
    int giveGem(vector<int>& gem, vector<vector<int>>& operations) {
        for (auto & op : operations)
        {
            int tmp = gem[op[0]] / 2;
            gem[op[0]] -= tmp;
            gem[op[1]] += tmp;
        }
        int max = 0, min = 1000;
        for (auto & it : gem)
        {
            max = it > max ? it : max;
            min = it < min ? it : min;
        }
        return max - min;
    }
};

参考答案

class Solution {
public:
    int giveGem(vector<int>& gem, vector<vector<int>>& operations) {
        for (auto &operation : operations) {
            int x = operation[0], y = operation[1];
            int number = gem[x] / 2;
            gem[x] -= number;
            gem[y] += number;
        }
        int mn = *min_element(gem.begin(), gem.end());
        int mx = *max_element(gem.begin(), gem.end());
        return mx - mn;
    }
};

感想

自然且简单

213. 打家劫舍 II(medium)

2023/9/17

00 : 25 : 01


题目描述

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

我的答案

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n < 4)
        {
            return *max_element(nums.begin(), nums.end());
        }
        vector<int> Inc(n, 0); // include 1
        vector<int> nInc(n, 0); // not include 1
        Inc[0] = nums[0]; nInc[0] = 0;
        Inc[1] = 0; nInc[1] = nums[1];
        Inc[2] = nums[0] + nums[2]; nInc[2] = nums[2];
        int res = Inc[2];
        for (int i = 3; i < n-1; ++i)
        {
            Inc[i] = max(Inc[i-2], Inc[i-3]) + nums[i];
            nInc[i] = max(nInc[i-2], nInc[i-3]) + nums[i];
            res = max(res, max(Inc[i], nInc[i]));
        }
        nInc[n-1] = max(nInc[n-3], nInc[n-4]) + nums[n-1];
        res = max(res, nInc[n-1]);
        return res;
    }
};

参考答案

class Solution {
    // 198. 打家劫舍
    int rob1(vector<int> &nums, int start, int end) { // [start,end) 左闭右开
        int f0 = 0, f1 = 0;
        for (int i = start; i < end; ++i) {
            int new_f = max(f1, f0 + nums[i]);
            f0 = f1;
            f1 = new_f;
        }
        return f1;
    }

public:
    int rob(vector<int> &nums) {
        int n = nums.size();
        return max(nums[0] + rob1(nums, 2, n - 1), rob1(nums, 1, n));
    }
};

感想

分成两段来执行打家劫舍1的操作很好

2560. 打家劫舍 IV(medium)

2023/9/19


题目描述

沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。

由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋

小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额

给你一个整数数组 nums 表示每间房屋存放的现金金额。形式上,从左起第 i 间房屋中放有 nums[i] 美元。

另给你一个整数 k ,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k 间房屋。

返回小偷的 最小 窃取能力。

示例 1:

输入:nums = [2,3,5,9], k = 2
输出:5
解释:
小偷窃取至少 2 间房屋,共有 3 种方式:
- 窃取下标 0 和 2 处的房屋,窃取能力为 max(nums[0], nums[2]) = 5 。
- 窃取下标 0 和 3 处的房屋,窃取能力为 max(nums[0], nums[3]) = 9 。
- 窃取下标 1 和 3 处的房屋,窃取能力为 max(nums[1], nums[3]) = 9 。
因此,返回 min(5, 9, 9) = 5 。

示例 2:

输入:nums = [2,7,9,3,1], k = 2
输出:2
解释:共有 7 种窃取方式。窃取能力最小的情况所对应的方式是窃取下标 0 和 4 处的房屋。返回 max(nums[0], nums[4]) = 2 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 1 <= k <= (nums.length + 1)/2

我的答案

class Solution {
public:
    int minCapability(vector<int>& nums, int k) {
        int lower = *min_element(nums.begin(), nums.end());
        int upper = *max_element(nums.begin(), nums.end());
        while (lower <= upper)
        {
            int mid = (lower + upper) / 2;
            int cnt = 0;
            bool passed = false;
            for (auto & num : nums)
            {
                if (passed)
                {
                    passed = false;
                    continue;
                }
                if (num <= mid)
                {
                    cnt++;
                    passed = true;
                }
            }
            if (cnt < k)
                lower = mid + 1;
            else
                upper = mid - 1;
        }
        return lower;
    }
};

参考答案

class Solution {
public:
    int minCapability(vector<int>& nums, int k) {
        int lower = *min_element(nums.begin(), nums.end());
        int upper = *max_element(nums.begin(), nums.end());
        while (lower <= upper) {
            int middle = (lower + upper) / 2;
            int count = 0;
            bool visited = false;
            for (int x : nums) {
                if (x <= middle && !visited) {
                    count++;
                    visited = true;
                } else {
                    visited = false;
                }
            }
            if (count >= k) {
                upper = middle - 1;
            } else {
                lower = middle + 1;
            }
        }
        return lower;
    }
};

感想

不是dp了, 是二分查找

LCP 06. 拿硬币(easy)

2023/9/20

00 : 01 : 21


题目描述

桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。

示例 1:

输入:[4,2,1]

输出:4

解释:第一堆力扣币最少需要拿 2 次,第二堆最少需要拿 1 次,第三堆最少需要拿 1 次,总共 4 次即可拿完。

示例 2:

输入:[2,3,10]

输出:8

限制:

  • 1 <= n <= 4
  • 1 <= coins[i] <= 10

我的答案

class Solution {
public:
    int minCount(vector<int>& coins) {
        int res = 0;
        for (auto & coin : coins)
        {
            res += (coin + 1) / 2;
        }
        return res;
    }
};

参考答案



感想

最简单的一题, 小学生都会做

2603. 收集树中金币(hard)

2023/9/21

00 : 29 : 47


题目描述

给你一个 n 个节点的无向无根树,节点编号从 0n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 aibi 之间有一条边。再给你一个长度为 n 的数组 coins ,其中 coins[i] 可能为 0 也可能为 11 表示节点 i 处有一个金币。

一开始,你需要选择树中任意一个节点出发。你可以执行下述操作任意次:

  • 收集距离当前节点距离为 2 以内的所有金币,或者
  • 移动到树中一个相邻节点。

你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。

如果你多次经过一条边,每一次经过都会给答案加一。

示例 1:

img

输入:coins = [1,0,0,0,0,1], edges = [[0,1],[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:从节点 2 出发,收集节点 0 处的金币,移动到节点 3 ,收集节点 5 处的金币,然后移动回节点 2 。

示例 2:

img

输入:coins = [0,0,0,1,1,0,0,1], edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[5,6],[5,7]]
输出:2
解释:从节点 0 出发,收集节点 4 和 3 处的金币,移动到节点 2 处,收集节点 7 处的金币,移动回节点 0 。

提示:

  • n == coins.length
  • 1 <= n <= 3 * 104
  • 0 <= coins[i] <= 1
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • edges 表示一棵合法的树。

我的答案

class Solution {
public:
    int collectTheCoins(vector<int>& coins, vector<vector<int>>& edges) {
        int n = coins.size();
        int remain = n;
        vector<vector<int>> graph(n);
        vector<int> degree(n, 0);
        for (auto & edge : edges)
        {
            graph[edge[0]].push_back(edge[1]);
            graph[edge[1]].push_back(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }
        queue<int> q;
        for (int i = 0; i < n; ++i)
        {
            if (degree[i] == 1 && !coins[i])
            {
                q.push(i);
            }
        }
        while (!q.empty())
        {
            int toDel = q.front();
            degree[toDel]--;
            remain--;
            q.pop();
            for (auto & it : graph[toDel])
            {
                degree[it]--;
                if (degree[it] == 1 && !coins[it])
                {
                    q.push(it);
                }
            }
        }

        for (int _ = 0; _ < 2; ++_)
        {
            for (int i = 0; i < n; ++i)
            {
                if (degree[i] == 1)
                {
                    q.push(i);
                }
            }
            while (!q.empty())
            {
                int toDel = q.front();
                degree[toDel]--;
                remain--;
                q.pop();
                for (auto & it : graph[toDel])
                {
                    degree[it]--;
                }
            }
        }
        return remain <= 1 ? 0 : (remain - 1) * 2;
    }
};

参考答案



感想

完全没有思路, 三分钟就看答案去了, 感觉确实想不到, 下次再不会继续看题解()

2591. 将钱分给最多的儿童(easy)

2023/9/22

00 : 13 : 31


题目描述

给你一个整数 money ,表示你总共有的钱数(单位为美元)和另一个整数 children ,表示你要将钱分配给多少个儿童。

你需要按照如下规则分配:

  • 所有的钱都必须被分配。
  • 每个儿童至少获得 1 美元。
  • 没有人获得 4 美元。

请你按照上述规则分配金钱,并返回 最多 有多少个儿童获得 恰好 8 美元。如果没有任何分配方案,返回 -1

示例 1:

输入:money = 20, children = 3
输出:1
解释:
最多获得 8 美元的儿童数为 1 。一种分配方案为:
- 给第一个儿童分配 8 美元。
- 给第二个儿童分配 9 美元。
- 给第三个儿童分配 3 美元。
没有分配方案能让获得 8 美元的儿童数超过 1 。

示例 2:

输入:money = 16, children = 2
输出:2
解释:每个儿童都可以获得 8 美元。

提示:

  • 1 <= money <= 200
  • 2 <= children <= 30

我的答案

class Solution {
public:
    int distMoney(int money, int children) {
        money -= children;
        if (money < 0)  return -1;
        int ans = money / 7;
        if (ans > children)
        {
            return children - 1;
        }
        else if (ans == children  && money % 7 > 0)
        {
            ans--;
        }
        else if (ans == children - 1 && money % 7 == 3)
        {
            ans--;
        }
        return ans;

    }
};

参考答案

class Solution {
public:
    int distMoney(int money, int children) {
        if (money < children) {
            return -1;
        }
        money -= children;
        int cnt = min(money / 7, children);
        money -= cnt * 7;
        children -= cnt;
        if ((children == 0 && money > 0) || (children == 1 && money == 3)) {
            cnt--;
        }
        return cnt;
    }
};

感想

贪心, 但是感觉更像是