第47场双周赛 2021-03-06

第一题

LC 1779. 找到最近的有相同 X 或 Y 坐标的点

难度:简单
算法:数组,扫描

题目描述:

给你两个整数 x 和 y ,表示你在一个笛卡尔坐标系下的 (x, y) 处。同时,在同一个坐标系下给你一个数组 points ,其中 points[i] = [ai, bi] 表示在 (ai, bi) 处有一个点。当一个点与你所在的位置有相同的 x 坐标或者相同的 y 坐标时,我们称这个点是有效的 。

请返回距离你当前位置 曼哈顿距离 最近的 有效 点的下标(下标从 0 开始)。如果有多个最近的有效点,请返回下标 最小 的一个。如果没有有效点,请返回 -1 。

两个点 (x1, y1) 和 (x2, y2) 之间的 曼哈顿距离 为 abs(x1 - x2) + abs(y1 - y2) 。

提示:
1 <= points.length <= 104
points[i].length == 2
1 <= x, y, ai, bi <= 104

题解:

简单的数组扫描记录答案。

class Solution {
public:
    int nearestValidPoint(int x, int y, vector<vector<int>>& points) {
        int ans = -1, len = 0x3f3f3f3f;
        int n = points.size();
        for(int i = 0; i < n; i++)
        {
            if(x == points[i][0] || y == points[i][1])
            {
                int t = abs(x - points[i][0]) + abs(y - points[i][1]);
                if(t < len)
                {
                    len = t;
                    ans = i;
                }
            }
        }
        return ans;
    }
};

第二题

LC 1780. 判断一个数字是否可以表示成三的幂的和

难度:中等
算法:位运算,枚举,DFS

题目描述:

给你一个整数 n ,如果你可以将 n 表示成若干个不同的三的幂之和,请你返回 true ,否则请返回 false 。

对于一个整数 y ,如果存在整数 x 满足 y == 3x ,我们称这个整数 y 是三的幂。

提示:
1 <= n <= 107

题解:

解法一:

观察数据范围,则n最大用315表示足够,可以枚举3的0~15次幂的选择情况,共216种,判断出所有情况中是否存在一种加和为n。

时间复杂度:O(216)

class Solution {
public:
    int num[18];
    bool checkPowersOfThree(int n) {
        for(int i = 0; i < 16; i++)  num[i] = pow(3, i);   
        for(int i = 0; i < 1 << 16; i++)
        {
            int t = 0;
            for(int j = 0; j < 16; j++)
            {
                if(i >> j & 1)  t += num[j];
            }
            if(t == n)  return true;
        }
        return false;
    }
};

解法二:位运算

考虑n的三进制表示,如果n可以表示为条件所述,那么其三进制中一定都是0或者1,也即当且仅当三进制表示中只有0或1,才可以表示为条件所述。
依次得到n的三进制表示的每一位,判断即可。

时间复杂度:O(n / 3)

class Solution {
public:
    bool checkPowersOfThree(int n) {
        while(n)
        {
            if(n % 3 == 2)  return false;
            n /= 3;
        }
        return true;
    }
};

第三题

LC 1781. 所有子字符串美丽值之和

难度:中等
算法:双指针,模拟

题目描述:

一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次数之差。

比方说,“abaacc” 的美丽值为 3 - 1 = 2 。
给你一个字符串 s ,请你返回它所有子字符串的 美丽值 之和。

提示:
1 <= s.length <= 500
s 只包含小写英文字母。

题解:

双指针i, j扫描子串头尾,j不断右移,对每个子串进行计算。动态统计出s[i~j]之间字符最高频率和最低频率,累计答案。

细节:在枚举子串时,由于要知道s[i~j]中每个字符出现次数,那么最好对于子串头i,让子串尾j从前向后移动,这样之前统计的次数后面可以利用到,省去每次在计算个数时还要重新扫描整个子串区间。

时间复杂度:O(n2 * 26)

class Solution {
public:
    int beautySum(string s) {
        int n = s.size(), ans = 0;
        for(int i = 0; i < n; i++)
        {
            vector<int> cnt(26);
            cnt[s[i] - 'a']++;
            for(int j = i + 1; j < n; j++)
            {
                int mx = 0, mi = 510;
                cnt[s[j] - 'a']++;
                for(int i = 0; i < 26; i++)
                {
                    if(cnt[i])
                    {
                        mx = max(cnt[i], mx);
                        mi = min(cnt[i], mi);
                    }
                }
                ans += (mx - mi);
            }
        }
        return ans;
    }
};

第四题

LC 1782. 统计点对的数目

难度:困难
算法:双指针,分类讨论,容斥原理

题目描述:

给你一个无向图,无向图由整数 n ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi] 表示 ui 和 vi 之间有一条无向边。同时给你一个代表查询的整数数组 queries 。

第 j 个查询的答案是满足如下条件的点对 (a, b) 的数目:

  • a < b
  • cnt 是与 a 或者 b 相连的边的数目,且 cnt 严格大于 queries[j] 。

请你返回一个数组 answers ,其中 answers.length == queries.length 且 answers[j] 是第 j 个查询的答案。

请注意,图中可能会有 重复边 。

提示:
2 <= n <= 2 * 104
1 <= edges.length <= 105
1 <= ui, vi <= n
ui != vi
1 <= queries.length <= 20
0 <= queries[j] < edges.length

题解:

首先考虑给出点a, b,如何计算出题中定义的cnt。可以实现根据edges计算出与每个点相连的边的数量,同时对于两端点之间有多少边相连。
则:cnt = count(a) + count(b) - count(a ↔ b)
题目要求对与每个queries[i]求出满足上式cnt > queries[i]的点对数量。
分情况来看:设 sum = sum1 + sum2

  • sum1:满足count(a) + count(b) - count(a ↔ b) > queries[i], 且count(a ↔ b) > 0
  • sum2:满足count(a) + count(b) - count(a ↔ b) > queries[i], 且count(a ↔ b) = 0

对于sum1可以借助edges数组得到 ,考虑sum2如何得到?
定义sum3,sum4如下:

  • sum3 : 满足count(a) + count(b) > queries[i]的点对数量
  • sum4 : 满足count(a) + count(b) > queries[i],且count(a ↔ b) > 0 的点对数量

根据容斥原理知:sum2 = sum3 - sum4

问题转化为如何求出sum3和sum4?
对于sum4仍然可以类似sum1,借助edges数组求出。
而sum3可以对cnt数组排序,抽离模型为在一个有序数组中求出有多少的二元组(i, j)满足cnt[i] + cnt[j] > queries[i],经典双指针扫描问题。
最后逐个击破,得到sum。

时间复杂度:O(len + m * (n + len)) 其中len为edges.length

#define MP(x, y) make_pair(x, y)
#define x first
#define y second
class Solution {
public:
    typedef pair<int, int> PII;
    vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
        vector<int> cnt(n + 1);
        map<PII, int> mp;
        for(auto& e : edges)
        {
            int a = e[0], b = e[1];
            cnt[a]++;
            cnt[b]++;
            if(a > b)   swap(a, b);
            mp[{a, b}]++;
            //mp[MP(a, b)]++;
        }
        vector<int> backup = cnt;
        sort(backup.begin(), backup.end());
        int m = queries.size();
        vector<int> res(m);
        for(int i = 0; i < m; i++)
        {
            int lim = queries[i], sum = 0;
            for(auto& [a, b] : mp)
            {
                int u = a.x, v = a.y;
                if(cnt[u] + cnt[v] - b > lim)   sum++;
                if(cnt[u] + cnt[v] > lim)   sum--;
            }
            int l = 1, r = n;
            while(l < r)
            {
                while(l < r && backup[l] + backup[r] <= lim)      l++;
                sum += (r - l);
                r--;
            }
            res[i] = sum;
        }
        return res;
    }
};

排名:243 / 3085(前7.88%)


你可能感兴趣的:(LeetCode周赛,算法,数据结构,leetcode,c++)