LeetCode第23場雙週賽(Biweekly Contest 23)解題報告

這周的雙週賽沒做,但補了題,發現其實不難,挺簡單的。

第一題:模擬。

第二題:構造。

第三題:計算幾何。

第四題:DP 或者 貪心。

詳細題解如下。


1. 統計最大組的數目(Count Largest Group)

        AC代碼(C++)

2. 構造 K 個迴文字符串(Construct K Palindrome Strings)

        AC代碼(C++)

3.圓和矩形是否有重疊(Circle And Rectangle Overlapping)

        AC代碼(C++)

4.做菜順序(Reducing Dishes)

        AC代碼(方法一、DP  C++)

        AC代碼(方法二、貪心  C++)


LeetCode第23場雙週賽地址:

https://leetcode-cn.com/contest/biweekly-contest-23/


1. 統計最大組的數目(Count Largest Group)

題目鏈接

https://leetcode-cn.com/problems/count-largest-group/

題意

給你一個整數 n 。請你先求出從 1 到 n 的每個整數 10 進製表示下的數位和(每一位上的數字相加),然後把數位和相等的數字放到同一個組中。

請你統計每個組中的數字數目,並返回數字數目並列最多的組有多少個。

示例 1:

輸入:n = 13
輸出:4
解釋:總共有 9 個組,將 1 到 13 按數位求和後這些組分別是:
[1,10],[2,11],[3,12],[4,13],[5],[6],[7],[8],[9]。總共有 4 個組擁有的數字並列最多。

提示:

  • 1 <= n <= 10^4

解題思路

根據題目意思,我們需要將 1 - n 這 n 個數進行分組,分組標準是,某數字的各位上 求和,和一樣的分爲同一組。

那麼根據數組範圍,我們發現,最大數位是 999,也就是說,求和之後,最大和也就是 27 ,那麼我們就可以用一個數組記錄 cnt[ i ] 記錄分到 i 組的數字個數,數組的下標 i 表示,數位求和的結果。

然後我們遍歷所有 cnt,得到 其中 cnt[ i ] 數量最多(也就是這個分組中,擁有的數字最多),並統計對應數量最多出現了多少次,也就是答案。

時間複雜度,我們要對每一個數字都求數位和,最多求 4 位,那麼就是 O(4 * n)。

AC代碼(C++)

class Solution {
public:
    int cnt[40];

    int cal(int n)  // 計算數位之和
    {
        int val = 0;
        while(n)
        {
            val += (n % 10);
            n /= 10;
        }
        return val;
    }

    int countLargestGroup(int n) {
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1;i <= n; ++i)
        {
            ++cnt[cal(i)];
        }

        int ans = 0;
        int mx = 0;
        for(int i = 0;i < 40; ++i)
        {
            if(cnt[i] > mx)  // 發現新的最大值,那麼更新
            {
                ans = 1;
                mx = cnt[i];
            }
            else if(cnt[i] == mx) ++ans;  // 不然如果 == 最大值,那麼出現次數 + 1
        }
        return ans;
    }
};

2. 構造 K 個迴文字符串(Construct K Palindrome Strings)

題目鏈接

https://leetcode-cn.com/problems/construct-k-palindrome-strings/

題意

給你一個字符串 s 和一個整數 k 。請你用 s 字符串中 所有字符 構造 k 個非空 迴文串 。

如果你可以用 s 中所有字符構造 k 個迴文字符串,那麼請你返回 True ,否則返回 False 。

示例 1:

輸入:s = "annabelle", k = 2
輸出:true
解釋:可以用 s 中所有字符構造 2 個迴文字符串。
一些可行的構造方案包括:"anna" + "elble","anbna" + "elle","anellena" + "b"

示例 2:

輸入:s = "leetcode", k = 3
輸出:false
解釋:無法用 s 中所有字符構造 3 個迴文串。

示例 4:

輸入:s = "yzyzyzyzyzyzyzy", k = 2
輸出:true
解釋:你只需要將所有的 z 放在一個字符串中,所有的 y 放在另一個字符串中。那麼兩個字符串都是迴文串。

提示:

  • 1 <= s.length <= 10^5
  • s 中所有字符都是小寫英文字母。
  • 1 <= k <= 10^5

解題思路

根據題意,我們統計每一個字母出現的個數。然後統計 中間 出現 奇數 的字母個數 c。

假設字母總個數爲 n (也就是字符串原來的長度)。

我們發現,如果 n < k,那麼相當於根據不夠分,也就是即使一個字母組成一個字符串,那麼也不夠 k 個,所以就直接返回 false。

然後我們發現,對於出現 奇數個數 的字母 個數 c,而根據迴文串的定義,按理來說,應該要求每一個字母,都是偶數偶數出現的,那麼出現奇數個的時候,就拿出一個(剩下就變成偶數了),那麼 這些 單獨出現的字母,一定是要放在一個迴文串的正中間,也就是說,k 個迴文串,最多容納 k 個奇數(放在正中間),所以要求 c <= k,那麼當 c > k 的時候,就 false。

上面只是直觀的想法,如果要數學證明,就是我們要證明

【證明:】如果 存在答案,那就是要求 c <= k <= n

【必要性】也就是,c <= k <= n 得到,存在答案。首先,當 c <= k 的時候,我們將 c 個 單獨出現的,組成 c 個 迴文串。那麼現在剩下 n - c 個字母,最少要組成 k - c 個迴文串。n - c >= k - c,並且,剩下 n - c 個字母 一定都是偶數次的。

我們零 a = k - c, b = n - c。也就是b 個字母,組成 a 個迴文串。 a <= b,並且 b 是偶數個字母。

那麼這個很簡單,如 aabbcc,最多可以組成 6 個迴文串,第一個 aabbcc,第二個 aabbc + c,第三個 aabb + c + c ,。。。

所以也就可以組成了。

【充分性】也就是,存在答案,得到 c <= k <= n。上面一開始的分析,就是正面了充分性。

AC代碼(C++)

class Solution {
public:
    int cnt[26];
    bool canConstruct(string s, int k) {
        if(s.size() < k) return false;
        
        memset(cnt, 0, sizeof(cnt));
        for(char c : s) ++cnt[c - 'a'];

        for(int i = 0;i < 26; ++i)
        {
            if(cnt[i] != 0 && cnt[i] % 2 == 1) --k;  // 如果出現奇數,那麼 --k (只有當 k >= 0,說明 c <= k)
        }
        
        if(k < 0) return false;
        return true;
    }
};

3.圓和矩形是否有重疊(Circle And Rectangle Overlapping)

題目鏈接

https://leetcode-cn.com/problems/circle-and-rectangle-overlapping/

題意

給你一個以 (radius, x_center, y_center) 表示的圓和一個與座標軸平行的矩形 (x1, y1, x2, y2),其中 (x1, y1) 是矩形左下角的座標,(x2, y2) 是右上角的座標。

如果圓和矩形有重疊的部分,請你返回 True ,否則返回 False 。

換句話說,請你檢測是否 存在 點 (xi, yi) ,它既在圓上也在矩形上(兩者都包括點落在邊界上的情況)。

示例 1:

示例有圖,具體可以看鏈接
輸入:radius = 1, x_center = 0, y_center = 0, x1 = 1, y1 = -1, x2 = 3, y2 = 1
輸出:true
解釋:圓和矩形有公共點 (1,0) 

示例 2:

示例有圖,具體可以查看鏈接
輸入:radius = 1, x_center = 0, y_center = 0, x1 = -1, y1 = 0, x2 = 0, y2 = 1
輸出:true

示例 3:

示例有圖,具體可以查看鏈接
輸入:radius = 1, x_center = 1, y_center = 1, x1 = 1, y1 = -3, x2 = 2, y2 = -1
輸出:false

提示:

  • 1 <= radius <= 2000
  • -10^4 <= x_center, y_center, x1, y1, x2, y2 <= 10^4
  • x1 < x2
  • y1 < y2

 

解題分析

根據,矩形和圓的關係,我們用矩形,將整個空間劃爲爲 9 部分。

第一部分 : 也就是圓心在矩形中,那麼就是說明,存在,true

第二部分:圓心在上下左右,那麼此時,要有公共點,那就是,要求,圓至少和 四條邊,有一條邊,相交 或者 相切(涉及到直線 和 圓 的關係)

第三部分:那麼只要求,圓和四個頂點,有一個頂點至少在圓內 或者 在 圓上。(那麼就是,點 和 圓 的位置關係)

AC代碼(C++)

const double EPS = 1e-6;

class Solution {
public:
    bool check(int d, int r)  // 直線和圓的位置關係
    {
        if(r >= d) return true;
        return false;
    }

    bool checkD(int r, int x0, int y0, int x1, int y1)  // 頂點和圓的位置關係
    {
        double d = sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1));
        if(r > d + EPS) return true;
        if(fabs(d - r) < EPS) return true;
        return false;
    }

    bool checkOverlap(int r, int x0, int y0, int x1, int y1, int x2, int y2) {
        // 第一部分:圓心在矩形中
        if(x0 >= x1 && x0 <= x2 && y0 >= y1 && y0 <= y2) return true;
        // 第二部分:要至少與其中一條邊相切或相交
        if(check(abs(y1 - y0), r) && x0 >= x1 && x0 <= x2) return true;
        if(check(abs(y2 - y0), r) && x0 >= x1 && x0 <= x2) return true;
        if(check(abs(x1 - x0), r) && y0 >= y1 && y0 <= y2) return true;
        if(check(abs(x2 - x0), r) && y0 >= y1 && y0 <= y2) return true;
        // 圓與四個頂點的位置關係
        if(checkD(r, x0, y0, x1, y1) || checkD(r, x0, y0, x1, y2) || checkD(r, x0, y0, x2, y1) || checkD(r, x0, y0, x2, y2)) return true;

        return false;

    }
};

4.做菜順序(Reducing Dishes)

題目鏈接

https://leetcode-cn.com/problems/reducing-dishes/

題意

一個廚師收集了他 n 道菜的滿意程度 satisfaction ,這個廚師做出每道菜的時間都是 1 單位時間。

一道菜的 「喜愛時間」係數定義爲烹飪這道菜以及之前每道菜所花費的時間乘以這道菜的滿意程度,也就是 time[i]*satisfaction[i] 。

請你返回做完所有菜 「喜愛時間」總和的最大值爲多少。

你可以按 任意 順序安排做菜的順序,你也可以選擇放棄做某些菜來獲得更大的總和。

示例 1:

輸入:satisfaction = [-1,-8,0,5,-9]
輸出:14
解釋:去掉第二道和最後一道菜,最大的喜愛時間係數和爲 (-1*1 + 0*2 + 5*3 = 14) 。每道菜都需要花費 1 單位時間完成。

示例 2:

輸入:satisfaction = [4,3,2]
輸出:20
解釋:按照原來順序相反的時間做菜 (2*1 + 3*2 + 4*3 = 20)

示例 3:

輸入:satisfaction = [-1,-4,-5]
輸出:0
解釋:大家都不喜歡這些菜,所以不做任何菜可以獲得最大的喜愛時間係數。

提示:

  • n == satisfaction.length
  • 1 <= n <= 500
  • -10^3 <= satisfaction[i] <= 10^3

解題分析

這道題,其實不難,首先是數據大小,使得不難。然後甚至暴力都可以解決。

根據題意 + 示例分析,我們可以知道,首先先要數組進行排序,也就是,我們要將最大值放到最後做(這樣子就可以保證 time 花的時間多,那麼乘 起來的 值,會盡可能大)。

同時,注意到 示例 3 中,也都可以不取。但不一定說是 負 的就不取,比如 示例 1,也是取了負值,但是可以拋棄部分大的負值。

方法一、DP

最先想到的就是DP,那麼我們設 dp[ i ][ j ],表示,前面 i 道 菜,取了 j 道 的最大值。

那麼狀態轉移方程,我麼 對於 dp[ i ][ j ] ,那麼我們此時,這道菜,拿 或者 不拿,不拿: dp[ i - 1][ j ]。拿 : dp[i - 1][ j - 1] + 當前滿足度 * j。所以 dp[ i ][ j ] = max(dp[ i - 1][ j ], dp[i - 1][ j - 1] + 當前滿足度 * j)

這裏有幾個特殊情況,如果 j == 0,那麼 dp[ i ][ 0 ]  = 0,也就是,一道菜沒拿。

如果 j == i,那就說明,全都拿了,那麼  = dp[ i - 1][ j - 1]  + 當前滿足度 * j。

初始化: 由於是最大值,那麼設的是最小值,根據題目意思,最小應該是 0,所以我們設的初始值都是 0

方法二、貪心

分析示例 1 ,當我們排序後之後,-9 -8 -1 0 5,我們發現,是從 -1 開始取,ye就是,我們本來想從 -9 開始取,但是從前到 後,累加 sum = -13 < 0,說明,前面的負數很多,正數不足,那麼這樣子,當我們從這個位置開始取得時候,就不得。

那麼從下一個 -8 開始,此時 sum = sum - (-9) = -4,還是不行

從 -1 開始,sum = sum - (-8) = 4,說明,此時後面正數夠了,那麼我們得想法,負數會被抵消,那麼我們儘可能讓 正數 * time 變大,那麼就從這個位置開始取了。

所以,我們遍歷從某個位置,只要從這個位置開始得 sum >= 0 ,那麼就是從這個位置,開始往後取。

 

其實方法二 可以看成,從最大開始取,那麼,我們只要往後取了,相當於 前面得那些數,都多加了一倍,那我們只要,從大數開始取,直到 sum < 0,那麼此時不再累加,因爲累加,導致 原本得數 + 負數  變小了。

AC代碼(方法一、DP  C++)

const int MAXN = 510;
class Solution {
public:
    int dp[MAXN][MAXN];
    int maxSatisfaction(vector<int>& sat) {
        int n = sat.size();
        sort(sat.begin(), sat.end());
        memset(dp, 0, sizeof(dp));

        for(int i = 1;i <= n; ++i)   //  遍歷每道菜
        {
            for(int j = 1;j <= i; ++j)   // 對於當前菜,可能是 才 拿了 j 道菜
            {
                if(j == i)  // j == i,說明,這道菜,一定要拿。
                {
                    dp[i][j] = dp[i - 1][j - 1] + sat[i - 1] * j;
                }
                else
                {
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + sat[i - 1] * j);
                }
            }
        }
        int ans = 0;
        for(int j = 1;j <= n; ++j) ans = max(ans, dp[n][j]);

        return ans;

    }
};

AC代碼(方法二、貪心  C++)

class Solution {
public:
    int maxSatisfaction(vector<int>& sat) {
        int n = sat.size();
        sort(sat.begin(), sat.end());
        int sum = 0;
        for(int num : sat) sum += num;
        int t = 1;
        int ans = 0;
        for(int i = 0;i < n; ++i)
        {
            if(sum < 0)
            {
                sum -= sat[i];
                continue;
            }
            ans += t * sat[i];
            ++t;
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章