題目連接數組
題目大意:分糖果,每一個小朋友都有一個ratings值,且每一個小朋友至少都要有一個糖果,並且每一個小朋友的ratings值若是比左右鄰舍的小朋友的ratings值高,則其糖果數量也比鄰舍的小朋友多。ide
法一:超時。按照要求,從前日後比較每一個小朋友的ratings值,若是後一個小朋友的ratings值比前一個大,則更新小朋友糖果值dp[i]=dp[i-1]+1;不然,將當前小朋友的糖果值置1,而後考察其前一個小朋友糖果值是否知足dp[i-1]<=dp[i],且ratings[i-1]>ratings[i],若是知足這兩個條件,則說明前小朋友的糖果值須要更新,且須要循環遍歷更新前面小朋友的糖果值。o(n^2)。代碼以下:spa
1 public int candy(int[] ratings) { 2 int len = ratings.length; 3 if(len == 0) { 4 return 0; 5 } 6 else if(len == 1) { 7 return 1; 8 } 9 int[] dp = new int[len]; 10 //初始化第一個小夥伴的糖果值 11 dp[0] = ratings[0] <= ratings[1] ? 1 : 2; 12 int cnt = 0; 13 for(int i = 1; i < len; i++) { 14 //只與前面小夥伴的ratings進行比較 15 if(ratings[i] > ratings[i - 1]) { 16 dp[i] = dp[i - 1] + 1; 17 } 18 else { 19 dp[i] = 1; 20 //若是前面小夥伴的糖果是1,且ratings比較高,則遍歷其前面的全部小夥伴 21 if(dp[i - 1] == 1 && ratings[i - 1] > ratings[i]) { 22 //更新前面的小夥伴的糖果值,由於這個更新,對於這種用例5,3,1,這個小夥伴前面的全部糖果值都要更新,因此進入下面的for循環進行判斷 23 dp[i - 1] = 2; 24 for(int j = i - 2; j >= 0; j--) { 25 //若是前面的小夥伴的糖果值小,且ratings又比較高,則更新其值 26 if(ratings[j] > ratings[j + 1] && dp[j] <= dp[j + 1]) { 27 dp[j] = dp[j + 1] + 1; 28 } 29 else { 30 break; 31 } 32 } 33 } 34 } 35 } 36 //計算全部的糖果值 37 for(int i = 0; i < len; i++) { 38 cnt += dp[i]; 39 } 40 return cnt; 41 }
法二(借鑑):兩個數組,一個數組從前日後遍歷,一旦ratings值比前一個大,則dp[i]=dp[i-1]+1;一個數組從後往前遍歷,一旦ratings值比後一個大,則dp[i]=dp[i+1]+1。最後從這兩個數組中取一個較大者,計算最終糖果值。o(n)。固然也能夠用一個數組,可是思想邏輯都是同樣的,只是若是用一個數組的話,就是從後往前遍歷獲得的新值與當前數組值進行比較,取較大者就是了。代碼以下(耗時5ms):3d
1 public int candy(int[] ratings) { 2 int len = ratings.length; 3 if(len == 0) { 4 return 0; 5 } 6 else if(len == 1) { 7 return 1; 8 } 9 int[] left_candy = new int[len]; 10 int[] right_candy = new int[len]; 11 //初始化 12 left_candy[0] = ratings[0] <= ratings[1] ? 1 : 2; 13 right_candy[len - 1] = ratings[len - 1] <= ratings[len - 2] ? 1 : 2; 14 //從前日後遍歷 15 for(int i = 1; i < len; i++) { 16 if(ratings[i] > ratings[i - 1]) { 17 left_candy[i] = left_candy[i - 1] + 1; 18 } 19 else { 20 left_candy[i] = 1; 21 } 22 } 23 //從後往前遍歷 24 for(int j = len - 2; j >= 0; j--) { 25 if(ratings[j] > ratings[j + 1]) { 26 right_candy[j] = right_candy[j + 1] + 1; 27 } 28 else { 29 right_candy[j] = 1; 30 } 31 } 32 //二者中取較大者,計算糖果值 33 int cnt = 0; 34 for(int i = 0; i < len; i++) { 35 cnt += Math.max(left_candy[i], right_candy[i]); 36 } 37 return cnt; 38 }
法三(借鑑):最優解。只遍歷一遍,o(n)。一旦遍歷到遞減rating,則計數遞減的個數,而暫停計算其糖果值;當遍歷到遞增rating時,則開始計算前面遞減的小朋友的糖果值,以及當前小朋友的糖果值。具體註釋看代碼。代碼以下(耗時5ms):code
1 public int candy(int[] ratings) { 2 int first = 1, cnt = 0, res = 1, len = ratings.length; 3 for(int i = 1; i < len; i++) { 4 //若是比前一個小朋友rating高,則計算總糖果值 5 if(ratings[i] >= ratings[i - 1]) { 6 //若是當前小朋友前面有遞減rating,先處理這幾個小朋友的糖果值 7 if(cnt > 0) { 8 //從遞減的第二個數開始,到最後一個遞減rating結束爲止,這幾個小朋友的糖果總值就是cnt * (cnt + 1) / 2 9 res += cnt * (cnt + 1) / 2; 10 //處理開始遞減的第一個數,即將其須要增長的糖果數cnt-res+1,加入res中 11 if(cnt >= first) { 12 res += cnt - first + 1; 13 } 14 //重置 15 cnt = 0; 16 first = 1; 17 } 18 //對於當前第i個小朋友,正常計算其糖果值,將其加入res結果中 19 first = (ratings[i] == ratings[i - 1]) ? 1 : first + 1; 20 res += first; 21 } 22 //計數遞減rating的個數 23 else { 24 cnt++; 25 } 26 } 27 //處理最後一組遞減rating,而其後沒有再反彈的小夥伴,即一直遞減,不知足ratings[i] >= ratings[i - 1]就到數組終結 28 if(cnt > 0) { 29 res += cnt * (cnt + 1) / 2; 30 if(cnt >= first) { 31 res += cnt - first + 1; 32 } 33 } 34 return res; 35 }