【每日算法】貪心決策原排序數組的每一位,以及貪心解的正確性證實|Python 主題月

本文正在參加「Python主題月」,詳情查看 活動連接html

題目描述

這是 LeetCode 上的 1846. 減少和從新排列數組後的最大元素 ,難度爲 中等git

Tag : 「貪心」github

給你一個正整數數組 arr 。請你對 arr 執行一些操做(也能夠不進行任何操做),使得數組知足如下條件:數組

  • arr 中 第一個 元素必須爲 1 。
  • 任意相鄰兩個元素的差的絕對值 小於等於 1 ,也就是說,對於任意的 1 <= i < arr.length (數組下標從 0 開始),都知足 abs(arr[i] - arr[i - 1]) <= 1 。abs(x) 爲 x 的絕對值。

你能夠執行如下 2 種操做任意次:markdown

  • 減少 arr 中任意元素的值,使其變爲一個 更小的正整數 。
  • 從新排列 arr 中的元素,你能夠以任意順序從新排列。

請你返回執行以上操做後,在知足前文所述的條件下,arr 中可能的 最大值 。app

示例 1:oop

輸入:arr = [2,2,1,2,1]

輸出:2

解釋:
咱們能夠從新排列 arr 獲得 [1,2,2,2,1] ,該數組知足全部條件。
arr 中最大元素爲 2 。
複製代碼

示例 2:post

輸入:arr = [100,1,1000]

輸出:3

解釋:
一個可行的方案以下:
1. 從新排列 arr 獲得 [1,100,1000] 。
2. 將第二個元素減少爲 2 。
3. 將第三個元素減少爲 3 。
如今 arr = [1,2,3] ,知足全部條件。
arr 中最大元素爲 3 。
複製代碼

示例 3:ui

輸入:arr = [1,2,3,4,5]

輸出:5

解釋:數組已經知足全部條件,最大元素爲 5 。
複製代碼

提示:spa

  • 1 <= arr.length <= 1 0 5 10^5

1 <= arr[i] <= 1 0 9 10^9

基本分析 & 證實

根據題意,數組的第一位必須是 1 1 ,且每一個數只能 減少不變,數值位置能夠任意調整。

求解通過調整後,符合要求的數組中的最大值是多少。

首先符合條件的數組相鄰位差值絕對值不超過 1 1 ,這限定了數組的必然是以下三種分佈之一:

  • (非嚴格)單調遞減
  • 存在波段
  • (非嚴格)單調遞增

證實一:取得最優解對應的數組「必然是」或者「可調整爲」(非嚴格)單調遞增的形式。

咱們使用反證法來證實另外兩種分佈不能取得最優解:

  • (非嚴格)單調遞減:題目限定了數的範圍爲正整數,且第一位爲 1 1 ,這種狀況不用討論了,跳過;
  • 存在波段:咱們始終能夠將波峯的右側出現的值,歸入到波峯的左側,從而消掉這個波峯,最終將整個分佈調整爲「(非嚴格)單調遞增」的形式,結果不會變差:

多個波段的狀況也是同理,能夠本身在紙上畫畫。

都是利用 波峯右側的點能夠調整成波峯左側的點,從而使分佈變爲(非嚴格)單調遞增。

至此,咱們證實了最優解對應的數組必然符合(非嚴格)單調遞增。

這啓發咱們能夠先對原數組排個序,在此基礎上進行分析。

對原數組排序獲得的有序數組,不必定是符合「相鄰位差值絕對值不超過 1 1 」的,同時因爲每一個數值能夠選擇 減少不變

證實二:當必需要對當前位進行調整的時,優先選擇調整爲「與前一值差值爲 1 1 的較大數」不會比調整爲「與前一差值爲 0 0 的較小數」更差。

這可使用概括推理,假設採起「優先調整爲與前一值差值爲 1 1 的較大數」獲得的序列爲 a,採用「優先調整與前一差值爲 0 0 的較小數」獲得的序列爲 b

根據「 a [ 0 ] = b [ 0 ] = 1 a[0] = b[0] = 1 」、「ab 長度一致」、「ab 均爲(非嚴格)單調遞增」以及「ab 均知足相鄰位差值不超過 1 1 」,可推導出 s u m ( a ) > = s u m ( b ) sum(a) >= sum(b) ,和任意位置 a [ i ] > = b [ i ] a[i] >= b[i] ,從而推導出 a 序列的最後一位必然大於等於 b 的最後一位。

b 不會比 a 更優。

證實三:調整大小的操做不會改變數組元素之間的相對位置關係。

在證實二的分析中,咱們會對某些元素進行「減少」操做,使得整個數組最終知足「相鄰位差值絕對值不超過 1 1 」。

但該證實成立的還有一個很重要的前提條件,就是調整操做不會出發元素的位置重排。

那麼該前提條件是否必然成立呢?答案是必然成立。

假設原排序數組中存在須要調整的點 i i 和點 j j ,且 n u m s [ i ] < = n u m s [ j ] nums[i] <= nums[j]

爲了讓數組知足條件,它們都進行了「減小」操做的調整,分別變爲了 p p q q ,若是觸發位置重排的話,必然有 n u m s [ p ] > = n u m s [ q ] nums[p] >= nums[q]

此時,咱們可以經過調整它們的變化關係:點 i i 變爲點 q q 、點 j j 變成點 p p 來確保一樣知足條件,且不觸發元素在有序數組中的位置重排。

貪心

排序,限定第一位值爲 1 1 ,從前日後處理,根據每一位是否「必須修改(與上一位差值是否大於 1 1 )」作決策,若是必須被修改,則修改成與前一值差值爲 1 1 的較大數。

Java 代碼:

class Solution {
    public int maximumElementAfterDecrementingAndRearranging(int[] arr) {
        int n = arr.length;
        Arrays.sort(arr);
        arr[0] = 1;
        for (int i = 1; i < n; i++) {
            if (arr[i] - arr[i - 1] > 1) {
                arr[i] = arr[i - 1] + 1;
            }
        }
        return arr[n - 1];
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int:
        n = len(arr)
        arr.sort()
        arr[0] = 1
        for i in range(1, n):
            if arr[i] - arr[i - 1] > 1:
                arr[i] = arr[i - 1] + 1
        return arr[n - 1]
複製代碼
  • 時間複雜度:假定 Arrays.sort 使用的是雙軸快排實現。複雜度爲 O ( n log n ) O(n\log{n})
  • 空間複雜度:假定 Arrays.sort 使用的是雙軸快排實現。複雜度爲 O ( log n ) O(\log{n})

最後

這是咱們「刷穿 LeetCode」系列文章的第 No.1846 篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,咱們將先把全部不帶鎖的題目刷完。

在這個系列文章裏面,除了講解解題思路之外,還會盡量給出最爲簡潔的代碼。若是涉及通解還會相應的代碼模板。

爲了方便各位同窗可以電腦上進行調試和提交代碼,我創建了相關的倉庫:github.com/SharingSour…

在倉庫地址裏,你能夠看到系列文章的題解連接、系列文章的相應代碼、LeetCode 原題連接和其餘優選題解。

相關文章
相關標籤/搜索