leetcode第186場周賽

目錄

 

第一題、分割字符串的最大得分

第二題、可獲得的最大點數

第三題、對角線遍歷

第四題、帶限制的子序列和


leetcode第186場周賽,時間2020/04/26 10:30-12:00

第一題、分割字符串的最大得分

難度:easy,鏈接:https://leetcode-cn.com/problems/maximum-score-after-splitting-a-string/

給你一個由若干 0 和 1 組成的字符串 s ,請你計算並返回將該字符串分割成兩個 非空 子字符串(即 左 子字符串和 右 子字符串)所能獲得的最大得分。

「分割字符串的得分」爲 左 子字符串中 0 的數量加上 右 子字符串中 1 的數量。

給你一個由若干 0 和 1 組成的字符串 s ,請你計算並返回將該字符串分割成兩個 非空 子字符串(即 左 子字符串和 右 子字符串)所能獲得的最大得分。

「分割字符串的得分」爲 左 子字符串中 0 的數量加上 右 子字符串中 1 的數量。

思路:暴力即可,用一個變量存儲左邊0的數量,用一個變量存儲右邊1的數量,然後遍歷。

class Solution {
    public int maxScore(String s) {
        int num1 = 0;//右邊1的數量,最開始左邊爲空,右邊是整個字符串,所以計算出整個字符串1的數量
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) == '1') num1++;
        }
        int num0 = 0;//左邊0的數量
        int res = 0;//結果
        for (int i = 0; i < s.length() - 1; i++){
            if (s.charAt(i) == '0'){
                num0++;
            }
            else {
                num1--;
            }
            res = Math.max(res,num0 + num1);
        }
        return res;
    }
}

 

第二題、可獲得的最大點數

難度:medium,鏈接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/

幾張卡牌 排成一行,每張卡牌都有一個對應的點數。點數由整數數組 cardPoints 給出。

每次行動,你可以從行的開頭或者末尾拿一張卡牌,最終你必須正好拿 k 張卡牌。

你的點數就是你拿到手中的所有卡牌的點數之和。

給你一個整數數組 cardPoints 和整數 k,請你返回可以獲得的最大點數。

輸入:cardPoints = [1,2,3,4,5,6,1], k = 3
輸出:12
解釋:第一次行動,不管拿哪張牌,你的點數總是 1 。但是,先拿最右邊的卡牌將會最大化你的可獲得點數。最優策略是拿右邊的三張牌,最終點數爲 1 + 6 + 5 = 12 。

思路:題目即等價爲從數組[cardPoints[len - k], cardPoints[len - k],cardPoints[len - k],...cardPoints[len - 1],cardPoints[0],cardPoints[1],...cardPoints[k - 1]]中找出大小爲k的子數組中和最大的,用滑動窗口滑動一下即可。代碼如下:

class Solution {
    public int maxScore(int[] cardPoints, int k) {
        int len = cardPoints.length;
        int res = 0;
        int sum = 0;
        int start = len - k;
        int end = len - 1;
        for (int i = start; i <= end; i++){
            sum += cardPoints[i];
        }
        res = sum;
        for (int i = 1; i <= k; i++){
            end = (end + 1) % len;
            sum += cardPoints[end] - cardPoints[start];
            res = Math.max(res,sum);
            start = (start + 1) % len;
        }
        return res;
    }
}

 

第三題、對角線遍歷

難度:medium,鏈接:https://leetcode-cn.com/problems/diagonal-traverse-ii/

給你一個列表 nums ,裏面每一個元素都是一個整數列表。請你依照下面各圖的規則,按順序返回 nums 中對角線上的整數。

輸入:nums = [[1,2,3],[4,5,6],[7,8,9]]
輸出:[1,4,2,7,5,3,8,6,9]

思路:題目讓輸出對角線上的整數,而同一對角線的不同數的共同點是row+col相同,因爲又要排序,所以結果數組輸出應該爲(row+col等於0的數,row+col等於1的數,row+col等於2的數。。。)。所以直接遍歷矩陣,根據矩陣的行列放入一個二維數組中即可,然後最後將二維數組取出來。然後再看按這種思路會得到什麼結果

[[1],[2,4],[3,5,7],[6,8],[9]],每個行加列相同的集合與結果輸出的順序相反,這是因爲遍歷的時候因爲先遍歷到行小的元素,所以行小的元素放在前面,但是結果卻是行小的元素放在最後,所以只要將每個行加列相等的集合逆序一下即可。

代碼如下:

class Solution {
    public int[] findDiagonalOrder(List<List<Integer>> nums) {
        int count = 0;//計算有多少數,以便最後res數組申請空間
        ArrayList<ArrayList<Integer>> arr = new ArrayList();
        for (int i = 0; i < nums.size(); i++)
        {
            for (int j = 0 ; j < nums.get(i).size(); j++)
            {
                if ((i + j + 1) > arr.size()){
                    arr.add(new ArrayList());
                    
                } 
                arr.get(i + j).add(nums.get(i).get(j));
            }
            count += nums.get(i).size();
        }
        int[] res = new int[count];
        int index = 0;
        for (int i = 0; i < arr.size(); i++)
        {
            for (int j = arr.get(i).size() - 1 ; j >= 0; j--)
            {
                res[index++] = arr.get(i).get(j);
            }
            
        }
        return res;
    }
}

 

第四題、帶限制的子序列和

難度:hard,鏈接:https://leetcode-cn.com/problems/constrained-subset-sum/

給你一個整數數組 nums 和一個整數 k ,請你返回 非空 子序列元素和的最大值,子序列需要滿足:子序列中每兩個 相鄰 的整數 nums[i] 和 nums[j] ,它們在原數組中的下標 i 和 j 滿足 i < j 且 j - i <= k 。

數組的子序列定義爲:將數組中的若干個數字刪除(可以刪除 0 個數字),剩下的數字按照原本的順序排布。

給你一個整數數組 nums 和一個整數 k ,請你返回 非空 子序列元素和的最大值,子序列需要滿足:子序列中每兩個 相鄰 的整數 nums[i] 和 nums[j] ,它們在原數組中的下標 i 和 j 滿足 i < j 且 j - i <= k 。

數組的子序列定義爲:將數組中的若干個數字刪除(可以刪除 0 個數字),剩下的數字按照原本的順序排布。

 

我的思路:看到最大子序列和就很容易想到用動態規劃來解決。動態規劃遞歸式也很容易想出來。

dp[i] 爲以nums[i]爲結尾的子序列的最大元素和,又因爲子序列中的上一個元素必然在{nums[i - 1],nums[i - 2],...,nums[i - k]}中,所以

dp[i] = max(dp[i - 1] + nums[i],dp[i - 2] + nums[i],dp[i - 3] + nums[i],...,dp[i - k] + nums[i])

所以可以寫出對應代碼:

class Solution {

    public int constrainedSubsetSum(int[] nums, int k) {

        int[] dp = new int[nums.length];

        dp[0] = nums[0];

        int res = dp[0];

        for (int i = 1; i < nums.length; i++)

        {

            dp[i] = nums[i];

            for (int j = Math.max(0,i - k); j < i; j++){

                dp[i] = Math.max(dp[i],dp[j] + nums[i]);

            }

            res = Math.max(res,dp[i]);

        }

        return res;

    }

}

分析時間複雜度,因爲k <= 數組大小,所以最壞情況下k可能會等於數組大小,假設數組大小爲N,最壞情況下時間複雜度會達到O(n^2),run了之後果然超時了,所以需要優化。

回到原來的動態規劃遞歸式:

dp[i] = max{dp[i - 1] + nums[i],dp[i - 2] + nums[i],dp[i - 3] + nums[i],...,dp[i - k] + nums[i]}

 

即dp[i] = max{dp[i-1] , dp[i - 2] , dp[i - 3] , ... , dp[i - k]} + nums[i] 

 

嘗試化簡,寫出dp[i - 1]的遞推式,看看兩者是否有關係,可否從dp[i - 1]推到dp[i]

 

dp[ i - 1] = max{dp[i - 2] , dp[i - 3] , ... , dp[i - k] ,dp[i - k - 1]} + nums[i - 1]

 

嘗試相減,不能直接相減,找兩者的共同點,發現兩者都有一段max {dp[i - 2],dp[i - 3] , dp[ i - k]},再將兩個dp遞推式轉化一下:

 

dp[i] - nums[i] = max{dp[i-1] ,** max {dp[i - 2] , dp[i - 3] , ... , dp[i - k]}**} 

 

dp[i - 1] - nums[i - 1] = max{**max{dp[i - 2] , dp[i - 3] , ... , dp[i - k]}** ,dp[i - k - 1]} 

 

找到了共同點之後發現不能直接從dp[i - 1]推到dp[i],但觀察一下,dp[i - 1]就是找dp[i- k - 1]到dp[ i - 2]的最大值,dp[i] 就是找dp[i - k]到dp[i - 1]的最大值,那這不就是大小爲k的滑動窗口嗎,用單調隊列即可以O(n)的時間複雜度求出所有窗口的最大值。

lc裏也有這道題:https://leetcode-cn.com/problems/sliding-window-maximum/

 

代碼如下:

```

class Solution {

    public int constrainedSubsetSum(int[] nums, int k) {

        int[] dp = new int[nums.length];

        dp[0] = nums[0];

        int res = dp[0];

        Deque<Integer> deque = new ArrayDeque();//單調遞減隊列,隊首是窗口的最大值,

        deque.addFirst(dp[0]);

        for (int i = 1; i < k; i++) //當窗口大小沒有達到k的時候

        {

            dp[i] = Math.max(deque.getFirst() + nums[i] , nums[i]);

            res = Math.max(res,dp[i]);

            while (!deque.isEmpty() && deque.getLast() < dp[i])

            {

                deque.removeLast();

            }

            deque.addLast(dp[i]);

        }

        for (int i = k; i < nums.length; i++)//當窗口大小達到k後

        {

            dp[i] = Math.max(deque.getFirst() + nums[i] , nums[i]);

            res = Math.max(res,dp[i]);

            while (!deque.isEmpty() && deque.getLast() < dp[i])

            {

                deque.removeLast();

            }

            deque.addLast(dp[i]);

            if (dp[i - k] == deque.getFirst()) deque.removeFirst();

        }

        return res;

    }

}

```

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章