leetcode1558題解【貪心】

leetcode1558.得到目標數組的最少函數調用次數

題目鏈接

演算法

貪心

時間複雜度O(nlogN)N為數組中最大的那個數。

1.題意就是給定一個函數,該函數有兩種功能,一種就是將數組中的所有數同乘以2,另一種就是將數組中的某個數加1。給定一個數組nums,讓你將初始值全為0的數組arr通過調用給定的函數來變成nums。問最少調用次數。

2.剛開始模擬了一番,但因為考慮的方法不對(至於哪裡不對呢,就是一開始我就把數組的值都加1了一遍,然後再同乘以2,最後再一個個補1,這麼做顯然不利於減少次數。至於當時為啥這麼想,估計是腦子抽了),最終無果。

3.接下來步入正題,我們可以這麼想。當初始值都為0時,我們可以使部分值首先加1(即最終要變成的較大的值),讓它們首先乘以2,不斷乘,乘到一定程度再處理。但這麼做有個問題,讓哪部分值先變成1呢?還有就是乘到哪種程度呢?

這個地方的確是個難點,不太容易想。如果直接模擬的話不太容易。

數組中誰乘2的次數最多,當然是目標值最大的那個數乘的次數最多,其他目標值較小的就相對來說乘的次數較少。我們可以貪心地讓那些乘的次數較少的包含在乘的次數最多裡面,至於它具體是怎麼乘的,我們不用考慮太多,這就是貪心演算法的好處。

如何計算每個數最終乘的次數,我們可以分情況討論:

為了方便,我們可以從目標值向初始值變化。

  • 對於奇數,可以先讓其變為偶數(變為偶數這個過程即減1,這個需要單獨記錄),然後再除2,每除一次就記錄一次。

  • 對於偶數,就除2,每除一次就記錄一次。可能除著除著就變成奇數了,比如250,這時候就執行上一步。

最終,數組中的值就都變成0了。

4.總之,這道題的基本思路就是求出目標數組中最大值變成0需要除2的次數,以及該數組中每個數需要減1的次數(什麼時候減,就是為奇數的時候減),二者相加即為答案。

C++程式碼

class Solution {
public:
    int minOperations(vector<int>& nums) {
        int len = nums.size();
        int res = 0;
        int mx = 0;     //記錄乘以2的最大次數
        for(int i = 0; i < len; i++){
            int cnt = 0;
            while(nums[i]){
                if(nums[i] % 2 == 1){
                    nums[i] -= 1;      //把它變成偶數
                    res++;
                }
                if(nums[i] > 0){
                    nums[i] /= 2;
                    ++cnt;
                }
            }
            mx = max(mx, cnt);
        }
        res += mx;
        return res;
    }
};

思路來源