Leetcode 第174场周赛 题解

Leetcode 第174场周赛 题解

方阵中战斗力最弱的 K 行

签到题,统计一下每行的军人数量,然后设置一下排序规则即可。

时间复杂度 \(O(nlogn)\)

typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define SHENZHUO 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 50003
#define X first
#define Y second

class Solution
{
public:
    struct p
    {
        int i;
        int val;
        bool operator < (p b)
        {
            if(val != b.val)
                return val < b.val;
            return i < b.i;
        }
    };
    vector<int> kWeakestRows(vector<vector<int>>& mat, int k)
    {
        vector<int> rnt;
        vector<p> tmp;
        _for(i,0,mat.size())
        {
            int cnt = 0;
            _for(j,0,mat[i].size())
                if(mat[i][j]==1)
                    cnt ++;
            tmp.pb({i,cnt});
        }
        sort(tmp.begin(),tmp.end());
        _for(i,0,k)
            rnt.pb(tmp[i].i);
        return rnt;
    }
};

数组大小减半

因为每次删除是选一个数,然后把这个数在数组中的全部都给删掉,每删一次数组长度减一,但是我们的消耗不管选谁,都是每次选一个数,算是每次代价恒定,要求删的越多越好,那选什么数?自然是选在数组中出现次数较多的数!也就是贪心算法啦。

统计一下每个数出现的次数,然后排个序,然后不断贪心去删,直到删到满足题目条件即可。

时间复杂度 \(O(nlogn)\)

typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define SHENZHUO 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 50003
#define X first
#define Y second

class Solution
{

    public:
        struct p
        {
            int num;
            int times;
            bool operator < (p b)
            {
                return times > b.times;
            }
        };
    int minSetSize(vector<int>& arr)
    {
        int n = arr.size();

        map<int,int> mp;
        _for(i,0,arr.size())
            mp[arr[i]] ++;
        vector<p> tmp;
        for(auto p:mp)
            tmp.pb({p.first,p.second});

        sort(tmp.begin(),tmp.end());
        int rnt = 0;
        int now = 0;
        _for(i,0,tmp.size())
        {
            now += tmp[i].times;
            rnt ++;
            if(now>=n/2)
                break;
        }
        return rnt;
    }
};

分裂二叉树的最大乘积

其实也没啥好说的,比较显而易见,如果只是删除一条边的话,只要枚举删除的这条边即可,主要就是分开以后的值的计算。

先预处理出一个变量 \(tol\) ,代表了这棵树的价值总和(所有节点值相加),然后再遍历一遍,假设当前节点为 \(x\) ,则可以尝试删除 \(x\) 连接左儿子的边和连接右儿子的边。左子树的价值总和我们可以通过递归回溯得到,其余一棵由 \(x\) 和他的右儿子以及树的其余部分的价值总和就是 \(tol-tol(left)\) ,也就是原树的价值总和减去左子树的价值总和,最后记得取余即可。

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

typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 50003
#define X first
#define Y second

class Solution
{
public:
    ll tol;
    ll rnt;
    void pre(TreeNode* root)
    {
        if(root->left)
        {
            pre(root->left);
            tol += root->left->val;
        }
        if(root->right)
        {
            pre(root->right);
            tol += root->right->val;
        }
    }
    ll dfs(TreeNode* root)
    {
        ll leftcnt = 0,rightcnt = 0;
        if(root->left)
            leftcnt = dfs(root->left);
        if(root->right)
            rightcnt = dfs(root->right);
        rnt = max(rnt,(rightcnt*(tol-rightcnt)));
        rnt = max(rnt,(leftcnt*(tol-leftcnt)));
        return leftcnt + rightcnt + root->val;
    }
    int maxProduct(TreeNode* root)
    {
        rnt = 0;tol = root->val;
        pre(root);
        dfs(root);
        rnt %= MOD;
        return rnt;
    }
};

跳跃游戏 V

跳跃游戏系列都是 \(dp\) 就不用我多说了吧(不是)

要求跳的次数最多,你想一下,你在乎他怎么跳吗?你并不在乎。如果从一个地方可以跳到另一个地方,我们只在乎他起跳的位置和从这里可以跳多少次。

所以有 \(dp[i][j]\) 表示当前在下标 \(i\) 处起跳,继续跳 \(j\) 次,值为 \(1\) 代表可行,\(0\) 代表不可行。这样如果我们能在 \(k \in [max(0,i-d),min(n-1,i+d)]\) 中间找到符合条件(不被挡住)的一个比他更高的地方 \(k\),跳到 \(i\) 处,那是不是 \(dp[k][j+1]\) 就可以从 \(dp[i][j]\) 转移过来?

这样我们先搞定初始状态,就是能不能跳一次,搞定以后我们去迭代更新 \(dp\) 数组就行了。

如果 \(dp[?][k] == 1\) ,则说明跳 \(k\) 次可行。

时间复杂度看起来是 \(O(n^3)\) ,但其实是 \(O(n^2)\),最里面一层不管对于什么情况都能快速 \(break\) 出来,所以其实是 \(O(maxn)=O(1)\),再加上一些优化 ( \(flag\) 可以在从哪儿都无法继续跳以后迅速 \(break\) ),\(1000\) 的数据量还是问题不大的。

typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define SHENZHUO 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 2003
#define X first
#define Y second

class Solution
{
public:
    int dp[maxn][maxn];
    int maxJumps(vector<int>& arr, int d)
    {
        int n = arr.size();
        memset(dp,0,sizeof(dp));
        int rnt = 0;
        //能否跳一次
        _for(i,0,n)
        {
            int le = max(0,i-d);
            int ri = min(n-1,i+d);
            for(int j = i+1; j <= ri; j ++)
            {
                if(arr[j] >= arr[i])
                    break;
                if(arr[j] < arr[i])
                    dp[i][1] = 1,rnt = 1;
            }
            for(int j = i-1; j >= le; j --)
            {
                if(arr[j] >= arr[i])
                    break;
                if(arr[j] < arr[i])
                    dp[i][1] = 1,rnt = 1;
            }
        }

        //跳k次
        _for(k,2,n)
        {
            int flag = 0;
            //枚举起跳位置i
            _for(i,0,n)
            {
                int le = max(0,i-d);
                int ri = min(n-1,i+d);
                for(int j = i+1; j <= ri; j ++)
                {
                    if(arr[j] >= arr[i])
                        break;
                    if(dp[j][k-1])
                    {
                        dp[i][k] = 1;
                        flag = 1;
                        break;
                    }
                }
                for(int j = i-1; j >= le; j --)
                {
                    if(arr[j] >= arr[i])
                        break;
                    if(dp[j][k-1])
                    {
                        dp[i][k] = 1;
                        flag = 1;
                        break;
                    }
                }
            }
            if(!flag)
                break;
            else
                rnt ++;
        }
        return rnt+1;
    }
};

原文地址:https://www.cnblogs.com/Asurudo/p/12251615.html

时间: 2024-10-04 06:28:04

Leetcode 第174场周赛 题解的相关文章

Leetcode 第175场周赛 题解(完结)

Leetcode 第175场周赛 题解 检查整数及其两倍数是否存在 数据范围小的可怜,\(O(n^2)\) 解法可行.如果范围大一点,可以先进行排序然后遍历每一个数进行二分查找,时间复杂度 \(O(nlogn)\) 代码是平方解法. typedef long long ll; typedef double db; #define _for(i,a,b) for(int i = (a);i < b;i ++) #define _rep(i,a,b) for(int i = (a);i > b;i

LeetCode 第 165 场周赛

LeetCode 第 165 场周赛 5275. 找出井字棋的获胜者 5276. 不浪费原料的汉堡制作方案 5277. 统计全为 1 的正方形子矩阵 5278. 分割回文串 III C 暴力做的,只能说数据不充分 找出井字棋的获胜者4 题目描述 Description A 和 B 在一个 3 x 3 的网格上玩井字棋. 井字棋游戏的规则如下: 玩家轮流将棋子放在空方格 (" ") 上. 第一个玩家 A 总是用 "X" 作为棋子,而第二个玩家 B 总是用 "

LeetCode 第 183 场周赛

LeetCode 第 183 场周赛 非递增顺序的最小子序列 降序排列后,往 vector<int>ans 中添加元素,直到其和超过所有元素和的一半. class Solution { public: vector<int> minSubsequence(vector<int>& nums) { const int n = nums.size(); sort(nums.begin(), nums.end(), greater<int>()); int

leetcode第 181 场周赛

5364. 按既定顺序创建目标数组 给你两个整数数组 nums 和 index.你需要按照以下规则创建目标数组: 目标数组 target 最初为空. 按从左到右的顺序依次读取 nums[i] 和 index[i],在 target 数组中的下标 index[i] 处插入值 nums[i] . 重复上一步,直到在 nums 和 index 中都没有要读取的元素. 请你返回目标数组. 题目保证数字插入位置总是存在. 示例 1: 输入:nums = [0,1,2,3,4], index = [0,1,

LeetCode第136场周赛

菜鸡我只做出了一道... 5055. 困于环中的机器人 在无限的平面上,机器人最初位于 (0, 0) 处,面朝北方.机器人可以接受下列三条指令之一: "G":直走 1 个单位 "L":左转 90 度 "R":右转 90 度 机器人按顺序执行指令 instructions,并一直重复它们. 只有在平面中存在环使得机器人永远无法离开时,返回 true.否则,返回 false. 提示: 1 <= instructions.length <=

LeetCode 第133场周赛总结

1029. 两地调度 公司计划面试 2N 人.第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]. 返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达. 示例: 输入:[[10,20],[30,200],[400,50],[30,20]] 输出:110 解释: 第一个人去 A 市,费用为 10. 第二个人去 A 市,费用为 30. 第三个人去 B 市,费用为 50. 第四个人去 B 市,费用为 20. 最低总费用为 10 + 30

LeetCode 第 149 场周赛

一.一年中的第几天(LeetCode-1154) 1.1 题目描述 1.2 解题思路 比较水的一题,搞清楚平年.闰年的判定规则,就很容易做出来. 1.3 解题代码 class Solution { public int ordinalOfDate(String date) { String[] dateArray = date.split("-"); int year = Integer.parseInt(dateArray[0]); int month = Integer.parse

LeetCode 第 155 场周赛

成绩 第二题尝试了两种解法,一种超时间.一种超内存,只AC了一题.归根结底还是太菜. 一.最小绝对差(LeetCode-5189) 1.1 题目描述 1.2 解题思路 数组排好序,获取最小的差值即可. 1.3 解题代码 public class Solution { class Test { public List<Integer> list; public Integer num; } public List<List<Integer>> minimumAbsDiff

leetcode 第184场周赛第一题(数组中的字符串匹配)

一.函数的运用 1,strstr(a,b); 判断b是否为a的子串,如果是,返回从b的开头开始到a的结尾 如“abcdefgh” “de” 返回“defgh”: 如果不是子串,返回NULL: 2,memcpy(a,b+n,c); 将b串从第n位后的c个字符串复制到a中,返回a串: (注:做完函数后需要添加上b[c] = '\0') 如果吧b+n换成b就是从头开始 将代码换成 memcpy(arr[cnt], words[i], strlen(words[i])+1); 就是直接复制words[i