1. 程式人生 > >LeetCode之Weekly Contest 93

LeetCode之Weekly Contest 93

rar ase tar top val amp ilo pda 分鐘

第一題:二進制間距

問題:

給定一個正整數 N,找到並返回 N 的二進制表示中兩個連續的 1 之間的最長距離。

如果沒有兩個連續的 1,返回 0

示例 1:

輸入:22
輸出:2
解釋:
22 的二進制是 0b10110 。
在 22 的二進制表示中,有三個 1,組成兩對連續的 1 。
第一對連續的 1 中,兩個 1 之間的距離為 2 。
第二對連續的 1 中,兩個 1 之間的距離為 1 。
答案取兩個距離之中最大的,也就是 2 。

示例 2:

輸入:5
輸出:2
解釋:
5 的二進制是 0b101 。

示例 3:

輸入:6
輸出:1
解釋:
6 的二進制是 0b110 。

示例 4:

輸入:8
輸出:0
解釋:
8 的二進制是 0b1000 。
在 8 的二進制表示中沒有連續的 1,所以返回 0 。

提示:

  • 1 <= N <= 10^9

鏈接:https://leetcode-cn.com/contest/weekly-contest-93/problems/binary-gap/

分析:

首先將數據轉換為二進制用vector存儲起來,然後在存儲數字vector中的1的坐標,新的坐標vector中相鄰兩個差值最大的就是最大距離。

如果0個數不到兩個,則返回0

AC Code:

 1 class Solution {
 2 public:
 3     int binaryGap(int N) {        
 4         int ret = 0;
 5         vector<int
> nums; //轉換為二進制數字,存儲每一位 6 while (N) 7 { 8 nums.emplace_back(N & 0x01); 9 N /= 2; 10 } 11 vector<int> diss;//記錄1的坐標 12 for (int i = 0; i < nums.size(); i++) 13 { 14 if (nums[i] == 1) 15 { 16 diss.emplace_back(i);
17 } 18 } 19 if (diss.size() <= 1) 20 { 21 return 0; 22 } 23 int f = diss[0]; 24 for (int i = 1; i < diss.size(); i++) 25 { 26 ret = max(ret, diss[i] - diss[i - 1]); 27 } 28 29 return ret; 30 } 31 };

其他:

目前看不到用時最短code,第一code:

 1     class Solution {
 2         public int binaryGap(int N) {
 3             int prev = -1;
 4             int ret = 0;
 5             for(int i = 0;i < 31;i++){
 6                 if(N<<~i<0){  
 7                     if(prev >= 0){
 8                         ret = Math.max(ret, i - prev);
 9                     }
10                     prev = i;
11                 }
12             }
13             return ret;
14         }
15     }

將數據左移0-30位,0左移30位還是0,1左移30位是0x80000000,最高位是符號位,如果N<<~i <0,說明當前最高位是1,思路其實和上面差不多,都是找到兩個1之間距離,更新取大值,不過我的是從低位算起,這個是從高位算起。

第一題:重新排序得到 2 的冪

問題:

從正整數 N 開始,我們按任何順序(包括原始順序)將數字重新排序,註意其前導數字不能為零。

如果我們可以通過上述方式得到 2 的冪,返回 true;否則,返回 false

示例 1:

輸入:1
輸出:true

示例 2:

輸入:10
輸出:false

示例 3:

輸入:16
輸出:true

示例 4:

輸入:24
輸出:false

示例 5:

輸入:46
輸出:true

提示:

  1. 1 <= N <= 10^9

鏈接:https://leetcode-cn.com/contest/weekly-contest-93/problems/reordered-power-of-2/

分析:

如果將數字按照每位數字進行重新排列組合後檢驗是否是2的冪,一來比較復雜,二來位數多了後排列組合可能太多。反向思考下,知道數據“長度”,找到符合該長度的可能的2的冪的數字,然後數字按照大小重新排序,如果一致則說明能組成2的冪。

AC Code:

 1 class Solution {
 2 public:
 3     bool reorderedPowerOf2(int N) {
 4         //先找到數字位數,得到該範圍內的合法數據,每個數字單獨排序,如果vector一致,則可以
 5         bool ret = false;
 6         vector<int> ornums;
 7         int length = 0;
 8         while (N)
 9         {
10             ornums.emplace_back(N % 10);
11             N /= 10;
12         }
13         sort(ornums.begin(), ornums.end());
14         
15         length = ornums.size();
16         vector<vector<int>> checks;
17         checks = getvalidnums(length);
18         int sameflag = true;
19         for (int i = 0; i < checks.size(); i++)
20         {
21             if (checks[i].size() == length)
22             {
23                 sameflag = true;
24                 for (int j = 0; j < checks[i].size(); j++)
25                 {
26                     if (checks[i][j] != ornums[j])
27                     {
28                         sameflag = false;
29                         break;
30                     }
31                 }
32                 //
33                 if (sameflag == false)
34                 {
35                     continue;
36                 }
37                 else
38                 {
39                     return true;                    
40                 }
41             }
42         }
43         return ret;
44         
45     }
46     vector<vector<int>> getvalidnums(int len)
47     {
48         vector<vector<int>> ret;
49         vector<int> tmp;
50         vector<int> tmpnum;
51         tmp = getvalidnum(len);
52         for (int i = 0; i < tmp.size(); i++)
53         {
54             int tmpdata = tmp[i];
55             tmpnum.clear();
56             while (tmpdata)
57             {
58                 tmpnum.emplace_back(tmpdata % 10);
59                 tmpdata /= 10;
60             }
61             sort(tmpnum.begin(), tmpnum.end());
62             ret.emplace_back(tmpnum);
63         }
64         return ret;
65     }
66     vector<int> getvalidnum(int len)
67     {
68         vector<int> ret;
69         int low = (int)pow(10, len - 1);
70         int high = (int)pow(10, len);
71         for (int i = 0; (int)pow(2, i) < high; i++)
72         {
73             if ((int)pow(2, i) >= low)
74             {
75                 ret.emplace_back((int)pow(2, i));
76             }
77         }
78         
79         return ret;
80     }
81     
82 };

其他:

第一code:

 1         classcl  Solution {
 2         
 3         int[] freq(char[] s)
 4         {
 5             int[] f = new int[10];
 6             for(char c : s){
 7                 f[c-0]++;
 8             }
 9             return f;
10         }
11         
12         public boolean reorderedPowerOf2(int N) {
13             char[] s = Integer.toString(N).toCharArray();
14             int[] f = freq(s);
15             
16             for(long i = 1;i <= 1<<30;i*=2){
17                 int[] g = freq(Long.toString(i).toCharArray());
18                 if(Arrays.equals(f, g))return true;
19             }
20             return false;
21         }
22     }

看起來像是JAVA,首先將數字轉換為字符串s,然後轉為數字數組f,f實際上記錄的是0-9每個數字出現的個數。在1-1<<30 範圍內(每次加倍),檢測得到的數字i的數字數組(即組成數字的0-9每個的個數)是否和f一致。

第三題:優勢洗牌

問題:

給定兩個大小相等的數組 AB,A 相對於 B 的優勢可以用滿足 A[i] > B[i] 的索引 i 的數目來描述。

返回 A 的任意排列,使其相對於 B 的優勢最大化。

示例 1:

輸入:A = [2,7,11,15], B = [1,10,4,11]
輸出:[2,11,7,15]

示例 2:

輸入:A = [12,24,8,32], B = [13,25,32,11]
輸出:[24,32,8,12]

提示:

  1. 1 <= A.length = B.length <= 10000
  2. 0 <= A[i] <= 10^9
  3. 0 <= B[i] <= 10^9

鏈接:https://leetcode-cn.com/contest/weekly-contest-93/problems/advantage-shuffle/

分析:

目的是想要贏得場次多,類似田忌賽馬,如果贏不了,直接拿最差的來應對。

首先將兩個隊伍按照實力排序,從強到弱AB對戰,如果A能贏則迎戰,否則A中最弱的來應對,A中的當前最強應對B的下一個。

AC Code:

 1 class Solution {
 2 public:
 3 vector<int> advantageCount(vector<int>& A, vector<int>& B) {
 4         vector<int> ret;
 5         vector<int> ShadowA(A);  //引用傳遞,避免修改到AB,其實用不到的
 6         vector<int> ShadowB(B);
 7         sort(ShadowA.begin(), ShadowA.end()); //先進行排序,方便對戰
 8         sort(ShadowB.begin(), ShadowB.end());
 9         vector<pair<int, int>> battles; //
10         //vector<int> Aleft; //本來想要的是如果B中的某一個A中剩余部分沒有人贏得了,跳過,結束後A中剩余部分直接和B中剩余部分隨便匹配,反正打不過。不過提交出錯後調試發現直接拿當前最弱的來應對就好了。
11         //vector<int> Bleft;
12         int ilow = 0;  //A中當前最弱的坐標
13         for (int i = ShadowA.size() - 1, j = ShadowB.size() - 1; i >= ilow, j >= 0;)
14         {
15 
16             if (ShadowA[i] > ShadowB[j])  //能贏,進行匹配
17             {
18                 pair<int, int> tmp{ ShadowB[j], ShadowA[i] };
19                 battles.emplace_back(tmp);
20                 i--;
21                 j--;
22                 //continue;
23             }
24             else
25             {
26                 //Bleft.emplace_back(ShadowB[j]);
27                 pair<int, int> tmp{ ShadowB[j], ShadowA[ilow] };  //打不過,直接拿A中最弱的一個來應對
28                 battles.emplace_back(tmp);
29                 ilow++;
30                 j--;
31                 //continue;
32             }
33 
34         }
35     
36         for (int i = 0; i < B.size(); i++)   //將交戰匹配對按照B中原有順序排列輸出A的出戰順序
37         {
38             for (int j = battles.size() - 1; j >= 0; j--)
39             {
40                 if (battles[j].first == B[i])
41                 {
42                     ret.emplace_back(battles[j].second);
43                     battles.erase(battles.begin() + j);
44                     break;
45                 }
46             }
47         }
48     
49         return ret;
50     }
51 };

其他:

第一code:

 1 class Solution {
 2         public int[] advantageCount(int[] a, int[] b) {
 3             int n = a.length;
 4             int[][] bi = new int[n][];
 5             for(int i = 0;i < n;i++){
 6                 bi[i] = new int[]{b[i], i};
 7             }
 8             Arrays.sort(bi, new Comparator<int[]>() {
 9                 public int compare(int[] a, int[] b) {
10                     return a[0] - b[0];
11                 }
12             });
13             Arrays.sort(a);
14             int p = 0;
15             int[] ra = new int[n];
16             Arrays.fill(ra, -1);
17             boolean[] used = new boolean[n];
18             for(int i = 0;i < n;i++){
19                 while(p < n && a[p] <= bi[i][0]){
20                     p++;
21                 }
22                 if(p < n){
23                     ra[bi[i][1]] = a[p];
24                     used[p] = true;
25                     p++;
26                 }
27             }
28             int q = 0;
29             for(int i = 0;i < n;i++){
30                 if(!used[i]){
31                     while(q < n && ra[q] != -1)q++;
32                     assert q < n;
33                     ra[q] = a[i];
34                 }
35             }
36             return ra;
37         }
38     }    

還是java,不太懂而且沒環境不太好調試查看,大概意思是對A進行排序,然後B中的每一個充A中找到最接近但比B大的數字來迎戰?

第二用的是C++:

 1 class Solution {
 2 public:
 3     vector<int> advantageCount(vector<int>& a, vector<int>& b) {
 4       int i,j,k,n,l;
 5       vector<pair<int,int> > bb;
 6       vector<pair<int,int> > aa;
 7       vector<int> ans;
 8       bb.clear();
 9       n=a.size();
10       for (i=0;i<n;i++)
11         bb.push_back(make_pair(b[i],i));
12       ans.clear();
13       for (i=0;i<n;i++)
14         ans.push_back(-1);
15       sort(a.begin(),a.end());
16       reverse(a.begin(),a.end());
17       sort(bb.begin(),bb.end());
18       reverse(bb.begin(),bb.end());
19       j=0;
20       for (i=0;i<a.size();i++)
21       {
22         while ((j<bb.size())&&(bb[j].first>=a[i])) j++;
23         if (j==bb.size()) break;
24         ans[bb[j].second]=a[i];
25         j++;
26       }
27       l=0;
28       for (k=i;k<a.size();k++)
29       {
30         while ((l<ans.size())&&(ans[l]!=-1)) l++;
31         ans[l]=a[k];
32         l++;
33       }
34       return ans;
35     }
36 };

第四題:最低加油次數

問題:

汽車從起點出發駛向目的地,該目的地位於出發位置東面 target 英裏處。

沿途有加油站,每個 station[i] 代表一個加油站,它位於出發位置東面 station[i][0] 英裏處,並且有 station[i][1] 升汽油。

假設汽車油箱的容量是無限的,其中最初有 startFuel 升燃料。它每行駛 1 英裏就會用掉 1 升汽油。

當汽車到達加油站時,它可能停下來加油,將所有汽油從加油站轉移到汽車中。

為了到達目的地,汽車所必要的最低加油次數是多少?如果無法到達目的地,則返回 -1

註意:如果汽車到達加油站時剩余燃料為 0,它仍然可以在那裏加油。如果汽車到達目的地時剩余燃料為 0,仍然認為它已經到達目的地。

示例 1:

輸入:target = 1, startFuel = 1, stations = []
輸出:0
解釋:我們可以在不加油的情況下到達目的地。

示例 2:

輸入:target = 100, startFuel = 1, stations = [[10,100]]
輸出:-1
解釋:我們無法抵達目的地,甚至無法到達第一個加油站。

示例 3:

輸入:target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
輸出:2
解釋:
我們出發時有 10 升燃料。
我們開車來到距起點 10 英裏處的加油站,消耗 10 升燃料。將汽油從 0 升加到 60 升。
然後,我們從 10 英裏處的加油站開到 60 英裏處的加油站(消耗 50 升燃料),
並將汽油從 10 升加到 50 升。然後我們開車抵達目的地。
我們沿途在1兩個加油站停靠,所以返回 2 。

提示:

  1. 1 <= target, startFuel, stations[i][1] <= 10^9
  2. 0 <= stations.length <= 500
  3. 0 < stations[0][0] < stations[1][0] < ... < stations[stations.length-1][0] < target

鏈接:https://leetcode-cn.com/contest/weekly-contest-93/problems/minimum-number-of-refueling-stops/

分析:

最開始采用貪心策略,在當前能跑到的範圍內找到油量最大的加油站,在那裏加油,最後幾分鐘純粹嘗試下,當然失敗了,局部的第二大未必就比另一個範圍的最大值小。

當初做LeetCode的初衷本來就是長長見識,避免徹底廢掉,上周的第四題折騰了好幾個晚上,到現在92周總結都還沒寫,所以這次沒準備糾結太多,稍微嘗試了下,比如從起點開始,得到所有可能的路徑,然後一直擴展

下去,最後得到所有能到達終點的策略,從中選擇加油次數最小的。

比如給出的例子中,100/10, [[10,60],[20,30],[30,30],[60,40]]

初始油量10,[0,10],10範圍內只有一個加油站沒得選,{[0,10],[10,60]},當前位置10,油量60,可以到到極限70,剩下加油站都可以到達,下一次擴展結果

0/10+10/60+20/30:當前油量:80,位置20,剩余裏程80

0/10+10/60+30/30:當前油量:70,位置30,剩余裏程70

0/10+10/60+60/40:當前油量:50,位置60,剩余裏程40

上面任意一個都可以到達目的地。

假設某一路線A一能夠到達目的地,剩余部分只要已經加油次數超過A的次數,即可舍去。

但是這樣漲下去可能爆內存,而且貪心時候給出的一個測例{ { 13, 21 }, { 26, 115 }, { 100, 47 }, { 225, 99 }, { 299, 141 }, { 444, 198 }, { 608, 190 }, { 636, 157 }, { 647, 255 }, { 841, 123 } };

嘗試手動模擬,結果數據太多難以模擬,而且懶得折騰了,直接參考第一code。

做法是首先得到當前能到達的所有加油站,按照油量排序存儲起來,在油量最多的那個裏面加油,然後再根據當前位置以及油量,找到所有能夠到達的位置,將加油站油量存儲起來。

通過優先隊列存儲油量,這樣的好處是自動在top位置存儲當前油量最多的加油站,由於隊列裏面存儲的加油站都是當前可達的,所以即使油量多的兩個加油站是相鄰的,也會在後面更新過程中用到,避免錯過局部最大值。

在{ { 13, 21 }, { 26, 115 }, { 100, 47 }, { 225, 99 }, { 299, 141 }, { 444, 198 }, { 608, 190 }, { 636, 157 }, { 647, 255 }, { 841, 123 } }例子中,target 1000,初始油量299

首先第一次可達前5個加油站,按照油量排序:299/141,26/115,225/99,100/47,12/21,

選擇299/141加油,此時位置299,油量299+141-299=141,剩余加油站[26/115,225/99,100/47,12/21],能夠到達最遠距離299+141=440(其實最遠距離就是油量累加和),沒有新的可以到達的加油站,從剩余加油站中選擇油量最大的,即26/115,

當前能夠到達440+115=555位置,新增加油站444/198,插入進去,當前剩余加油站[444/198,225/99,100/47,12/21],選擇最大油量198,

則當前位置444,能夠到達最遠距離299+141+115+198=753,新增加油站後剩余加油站 [647/255,608/190,636/157,225/99,100/47,12/21 ],選擇最大油量647/255

則此時能到達最遠距離753+255=1008,達到target1000,則最終選擇的加油站是26/115,299/141,444/198,647/255,即需要加油4次。

AC Code:

 1 class Solution {
 2 public:
 3 int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
 4         priority_queue<int> queue;
 5         int ret = 0;
 6         int p = 0; //加油站下標
 7         int d = startFuel; //當前能到達的位置
 8         while (true)
 9         {
10             while (p < stations.size() && stations[p][0] <= d)  //將當前能到達的加油站都存儲起來
11             {
12                 queue.push(stations[p][1]);
13                 p++;
14             }
15             if (d >= target)  //當前油量能夠到達終點
16             {
17                 return ret;
18             }
19             if (queue.empty() == 1)
20             {
21                 return -1;
22             }
23             d += queue.top();  //在能到達的加油站中取油量最大的一個
24             queue.pop();
25             ret++;
26         }
27     }
28 };

其他:

1.需要熟悉STL 中的一些基本性質,比如C++中priorty_queue是大堆,Java中貌似是最小堆,所以第一的code中需要存儲-XXX來變相將小堆當做大堆用。

2.了解貪心算法的局限性

3.需要提高建模能力,好多問題本質上都是數學問題。

總結:

答出了三題,不過這一次題目相對簡單,之前一次答出三題,提交錯誤好幾次都能200+名,這次就提交錯誤兩次都600+名了,雖然LeetCode周賽只是用來練習,能夠排名靠前也是好的,難題要能做出來,簡單題要能做的快。就目前個人能力來說,15+20+20+35的時間分配比較合理,而且一般來說最後30分鐘還基本上都用到了前面三個題中,希望年前能夠AK一次。

LeetCode之Weekly Contest 93