【Leetcode C++ 解題筆記】Greedy - part 1
===
[TOC]
本筆記僅供個人學習用途,內容斟酌參考。
## [409. Longest Palindrome](https://leetcode.com/problems/longest-palindrome/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : hashtable, string, greedy.
題目敘述:
給定一個由大寫或小寫所組成的字串 s,回傳那些可以由字母所組成最長回文的長度。
字母是區分大小寫的,如 `Aa` 不被視為一種回文。
解題思路:
建立一個 hashtable (unordered_map),計算每個字母所出現的次數。
- 如果次數是偶數:表示可以對稱放在回文的左右兩邊,可以全部使用,所以直接將次數加進去 len 裡面。
- 若次數是奇數:因為回文有對稱性,所以只能用偶數部分(次數 - 1)放在左右兩邊,剩下的字母可置於回文中間。(整個回文只能有一個這樣的字母在中間)
solution ( 0 ms ):
```cpp=
class Solution {
public:
int longestPalindrome(string s) {
unordered_map <char, int> f;
for (char c : s){
f[c]++;
}
int len = 0;
bool odd_found = false;
for (auto& p : f){
if (p.second % 2 == 0){
len += p.second;
}
else{
len += p.second - 1;
odd_found = true;
}
}
if (odd_found) len++;
return len;
}
};
```
## [2037. Minimum Number of Moves to Seat Everyone](https://leetcode.com/problems/minimum-number-of-moves-to-seat-everyone/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Array, Greedy, Sorting, Counting Sort
題目敘述:
有 n 個空座位及 n 個學生,他們都站在一個房間裡。給定一個長度為 n 的陣列 seats,`seats[i]` 為在第 i 個座位的位置;也給定一個長度為 n 的陣列 students,`students[j]` 為在第 j 個學生的位置。
然後叫你紀錄從 `seats[i]` 到 `students[j]` 的距離是多少,回傳將這些距離加起來最短的和。
解題思路:
sort 兩個 vector,迴圈跑一次算 `abs(seats[i] - students[i])`,相加後就是答案。
solution ( 0ms ) :
```cpp=
class Solution {
public:
int minMovesToSeat(vector<int>& seats, vector<int>& students) {
sort(seats.begin(), seats.end());
sort(students.begin(), students.end());
int move = 0;
for (int i = 0; i < seats.size(); i++){
move += abs(seats[i] - students[i]);
}
return move;
}
};
```
## [1221. Split a String in Balanced Strings](https://leetcode.com/problems/split-a-string-in-balanced-strings/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : String, Greedy, Counting
題目敘述:
所謂平衡字串(Balanced string)就是兩字元 `'L', 'R'` 的數量相等。
給定一個字串 s,拆分成一些子字串使得:
- 每個子字串都被平衡
回傳最大可以獲得的平衡字串數量。
解題思路:
遍歷 s,看要設定成是遇到 L 就 ++,還是 R 就 ++,都可以,反正只要一增,另外一個就減。
每次遍歷要檢查 balance 是否 == 0,是的話表示平衡,count++。
solution ( 0ms ) :
```cpp=
class Solution {
public:
int balancedStringSplit(string s) {
int count = 0, balance = 0;
for (const char& c : s){
if (c == 'L'){
balance++;
}
else{
balance--;
}
if (balance == 0){
count++;
}
}
return count;
}
};
```
## [2160. Minimum Sum of Four Digit Number After Splitting Digits](https://leetcode.com/problems/minimum-sum-of-four-digit-number-after-splitting-digits/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Math, Greedy, Sorting
題目敘述:
給四位數正整數,將四位數拆分成四個一位數,求將這些一位數排列組合後,使得 new1 跟 new2 的總和最小?
new1 和 new2 最多可以是三位數,最低為一位數,兩位數與兩位數也可。
如 2932 拆成 2、9、3、2,可以組成 29、32 相加,也可以組成 22、39 相加,求得兩者相加的最小和。
解題思路:
用 while 迴圈,四位數取模(`%10`)的結果放入陣列裡面,接下來再對原四位數砍一位數(`num /= 10`),再繼續取模,直到迴圈跑完為止。
最後是 new1 跟 new2 的相加,一位數加上三位數肯定比兩位加兩位大,所以這兩個變數都是兩位加兩位。
但是在此之前可以先排序陣列,比較好操作。new1 跟 new2 的十位數一定要取最小的那兩個,之後個位數隨便怎麼排都沒差。
Solution ( 0 ms ) :
```cpp=
class Solution {
public:
int minimumSum(int num) {
int i = 4;
vector <int> nums(i);
while (i--){
nums[i] = num % 10;
num /= 10;
}
sort(nums.begin(), nums.end());
int d1 = nums[0]*10 + nums[2];
int d2 = nums[1]*10 + nums[3];
return d1 + d2;
}
};
```
## [2864. Maximum Odd Binary Number](https://leetcode.com/problems/maximum-odd-binary-number/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Math, String, Greedy
題目敘述:
給定一個必定至少含有一個 1 的字串 s。
你必須要重新排列這些位元,使其組合成一個最大的奇數二進位數字。
回傳一個字串,表示可以從給定組合中得到的最大奇數二進位數字。
解題思路:
設兩個變數,用 range-based for loop 去計算 1 跟 0 在字串 s 的次數。
因為是「二進位」,所以能不能是奇數,在最末端是致關重要的一件事,如:`0101`,最後面的 1 意即 $1 \times 2^0 = 1$ 。
因此,在算完次數後,記得要再讓紀錄 1 次數的變數 `--`,因為那一次被拿去放到最後面了。
最後用 result 字串變數儲存結果,在算完 1 後加上剩下的 0,最後再補個 1。
Solution ( 0 ms ) :
```cpp=
class Solution {
public:
string maximumOddBinaryNumber(string s) {
int count_1 = 0, count_0 = 0;
for (char c : s){
if (c == '1') count_1++;
else count_0 ++;
}
count_1--;
string result(count_1, '1');
result += string(count_0, '0');
result += '1';
return result;
}
};
```
## [1323. Maximum 69 Number](https://leetcode.com/problems/maximum-69-number/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Math, Greedy
這是一個蠻有趣的題目XD
題目敘述:
給定一個僅由 6 跟 9 所組成的正整數 num。
你只能改變一個位數的狀態,如 6 改成 9,9 改成 6,試找出改完後最大的值為何?
解題思路:
將位數存入陣列還蠻不實際的,反而轉成字串更快、更直觀。
用 `to_string()` 轉成字串後,range-based for loop 遍歷字串第一個出現 6 的字元,改成 9 後直接 break,然後再把改完的字串換成整數,結束。
Solution ( 0 ms ) :
```cpp=
class Solution {
public:
int maximum69Number (int num) {
string s = to_string(num);
for (char& c : s){
if (c == '6'){
c = '9';
break;
}
}
return stoi(s);
}
};
```
## [1827. Minimum Operations to Make the Array Increasing](https://leetcode.com/problems/minimum-operations-to-make-the-array-increasing/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Array, Greedy
題目敘述:
給定一個整數陣列(索引以 0 作為起始點),可任選一個位置的元素,使其增加 1,讓這個陣列成一個嚴格遞增的陣列。
回傳這樣的最小操作次數。
解題思路:
遍歷 vector,檢查 `nums[i] <= nums[i - 1]`,若成立,算出這兩者的增加量:`nums[i - 1] + 1 - nums[i]`,為什麼要算呢?因為這是要拿去判斷有多少操作次數的,所以之後要再把 `operations` 次數加上這個增加量。
之後就是直接做遞增的動作了。
Solution ( 10 ms ) :
```cpp=
class Solution {
public:
int minOperations(vector<int>& nums) {
int operations = 0;
for (int i = 1; i < nums.size(); i++){
if (nums[i] <= nums[i-1]){
int temp = nums[i - 1] + 1 - nums[i];
operations += temp;
nums[i] = nums[i - 1] + 1;
}
}
return operations;
}
};
```
## [561. Array Partition](https://leetcode.com/problems/array-partition/?envType=problem-list-v2&envId=greedy)
這題真的簡單到哭...害我以為真的就這樣嗎?
difficulty : Easy.
tags : Array, Greedy, Sorting, Counting Sort
題目敘述:
給定一個包含 2n 個整數的整數陣列 nums,將這些整數分組為 n 對 `(a1, b1), (a2, b2), ..., (an, bn)`,使得所有 i 的 `min(ai, bi)` 總和最大化。回傳最大化的和。
解題思路:
排序,然後 for 的遞增條件改成 `i += 2`,`sum += nums[i];` 結束。
Solution ( 15 ms ):
註:這個速度蠻讓人好奇的,我的寫法跟 0ms 完全一樣啊??
```cpp=
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int sum = 0;
for (int i = 0; i < nums.size(); i += 2){
sum += nums[i];
}
return sum;
}
};
```
## [2656. Maximum Sum With Exactly K Elements](https://leetcode.com/problems/maximum-sum-with-exactly-k-elements/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Array, Greedy
題目敘述:
給定一個索引為 0 作為起始點的整數陣列 nums 和一個整數 k。你的任務是執行以下操作 k 次,來最大化你的分數:
1. 從 nums 選擇一個元素 m。
2. 將所選的 m 從陣列中移除。
3. 新增一個新元素到陣列裡面,其值為 m + 1。
4. 分數增加為 m。
回傳執行該操作恰好 k 次後可獲得的最高分數。
解題思路:
我不確定該題的陣列是否都排序好,保險起見可以用 `max_element(first, last)` 求最大值。
由於每次更新時都是 m + 1,所以也就成了公差為 1 的等差級數。
公式:
$$S_n = \frac{(a_1 + a_n) \times n}{2}$$
所以解題思路就是代公式,結束。
Solution ( 0ms ) :
```cpp=
class Solution {
public:
int maximizeSum(vector<int>& nums, int k) {
int m = *max_element(nums.begin(), nums.end());
return (m + m + k - 1) * k / 2; // k 是項數,記得分子那邊要減個 1。
}
};
```
## [2697. Lexicographically Smallest Palindrome](https://leetcode.com/problems/lexicographically-smallest-palindrome/description/?envType=problem-list-v2&envId=greedy)
difficulty : Easy.
tags : Two pointers, String, Greedy
題目敘述:
給定一個由小寫英文字母組成的字串 `s`,你可以多次將其中一個字元替換成另一個小寫字母。
請用最少的操作次數將 `s` 轉換成回文。若有多種解法,請回傳字典序最小的那一個回文字串。
回傳轉換後的回文字串。
解題思路:
使用雙指標 left, right,根據回文特性,若左右兩邊字元不相等,那就繼續下一個判斷條件(題目要求字典序最小)判斷字典序(`s[left] < s[right]` 或 `s[left] > s[right]`),去做相對應的字元賦值。
Solution ( 0ms ) :
```cpp=
class Solution {
public:
string makeSmallestPalindrome(string s) {
int left = 0, right = (int)s.length() - 1;
while (left < right){
if (s[left] != s[right]){
s[left] = s[right] = (s[left] < s[right]) ? s[left] : s[right];
}
left++;
right--;
}
return s;
}
};
```