目录
讀題
1005.K次取反后最大化的数组和
自己看到题目的第一想法
看完後的想法就是,將數組排列後,執行k次迴圈,將負數轉為正數,並記錄最小值,假設k次迴圈大於nums中的負數數量,則後續只將最小值進行正負數轉換,實際寫題遇到很多問題,經過調整後雖然code不精簡,但可以通過。
看完代码随想录之后的想法
看完之後的確自己的寫法太不精簡了,也沒有歸納出兩次的貪心策略
- 絕對值排序,將最大的負數進行取反操作
- 全部為正後,k沒有消耗完,則對最小的數值進行反覆取反操作
裡面有個部分我看到之後就學到了,就是對於第二個貪心策略,如果沒看影片前我真的會使用迴圈進行反覆取反的操作,但看完之後,才發現只需要一個判斷奇數偶數來對取反操作進行一個快速判斷。
134. 加油站
自己看到题目的第一想法
看得有點矇,不太知道要怎麼下手這一題,不像135. 分发糖果或1005.K次取反后最大化的数组和至少有個思路,這個看了二十分鐘,沒有想法。
看完代码随想录之后的想法
看完之後,看到total 跟 當前的想法我覺得真的太妙了,方法一還要再花點時間進行理解,不過方法二的做法很直覺,假設我前面的i的剩餘油量為負,那我就從i+1重新開始,跟之前最大子序和的部分有點像,並且假設我總油量大於消耗油量,那一定可以跑完全程,局部最優的策略就是假設我當前的油量為正那我就繼續執行,假設為負,那就從下一個點重新開始。
135. 分发糖果
自己看到题目的第一想法
看到題目感覺跟擺動有點像,就用一個陣列紀錄是否有變化,之後用這個陣列去針對count做++或count = 1的變換,但實際操作發現假設在一個中間有平坡的狀況會出現問題,
看完代码随想录之后的想法
看完之後就知道我自己是一次判斷左右來進行解題的,就像我在第一次解題時,會出現中間有平坡或者前後遍歷的差異,導致實際上分配是有差別的,因為要比較相鄰的元素,跟著影片看完講解後就對這道題目比較理解了。
1005.K次取反后最大化的数组和 - 實作
思路
一開始思路
- 將數組排列後,執行k次迴圈
- 將負數轉為正數,並記錄最小值
- 假設k次迴圈大於nums中的負數數量
- 後續只將最小值進行正負數轉換
代碼隨想錄思路
- 貪心策略
- 絕對值排序,將最大的負數進行取反操作
- 全部為正後,k沒有消耗完,則對最小的數值進行反覆取反操作
- 進行絕對值排序
- 遍歷所有元素,假設小於零則取反
- 對於剩餘的k進行奇數偶數判斷,假設為奇數則對最後一個數值(最小的數值)取反操作。
- get 數組和
Code
一開始代碼
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
int sum = 0;
int min = 101;
int remain = 0;
for(int count = 0, i = 0; count < k; count++) {
if(i < nums.size()) {
int mul = 0;
if(nums[i] < 0 ){
nums[i] = nums[i] * -1;
mul = 1;
}
if(min > nums[i]) {
min = nums[i];
remain = i;
}
if(mul != 1) nums[remain] = nums[remain] * -1;
i++;
} else {
nums[remain] = nums[remain] * -1;
}
}
for(int i = 0; i < nums.size(); i++) {
printf("nums[%d]:%d\\n",i, nums[i]);
sum += nums[i];
}
return sum;
}
};
看完代碼隨想錄的代碼
class Solution {
static bool cmp(int a, int b) {
return abs(a) > abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end(), cmp);
for(int i = 0; i < nums.size(); i++) {
if(nums[i] < 0 && k > 0) {
nums[i] *= -1;
k--;
}
}
if(k%2 == 1) nums[nums.size() - 1] *= -1;
int sum = 0;
for(int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
return sum;
}
};
134. 加油站 - 實作
思路
- curSum 記錄到目前為止的油量
- totalSum 記錄整趟旅程的剩餘油量
- start 紀錄起始位置
- 遍歷所有元素
- 假設curSum < 0,則start 從下一個元素開始,並且初始化curSum;
- 假設totalSum < 0 代表不可能走完一圈,否則return start。
Code
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for(int i = 0; i < gas.size(); i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if(curSum < 0) {
start = i + 1;
curSum = 0;
}
}
if(totalSum < 0) return -1;
return start;
}
};
135. 分发糖果 - 實作
思路
錯誤思路
- 建立一個數組,紀錄左右的變化
- 假設diff == true 則count++ 並且加到result,否則count = 1 並加到result
- return result;
錯誤點: 單向遍歷,中間有平坡之類的狀況沒有考慮到
正確思路
- 建立Candy數組紀錄要發多少糖果,全部預設為1
- 由前往後遍歷,假設右比左大,當前的Candy數量為上一個Candy數量+1
- 由後往前遍歷,假設左比右大,比較與由前往後遍歷的Candy數量與當前的Candy數量為上一個Candy數量+1哪個較大,取較大的數值
- 計算Candy數組和,並回傳結果。
Code
錯誤思路代碼
class Solution {
public:
int candy(vector<int>& ratings) {
vector<bool> diff(ratings.size(), false);
for(int i = 1; i < ratings.size(); i++) {
if(ratings[i] - ratings[i - 1] > 0) diff[i] = true;
else if (ratings[i] - ratings[i - 1] < 0) diff[i - 1] = true;
}
int count = 1;
int result = 0;
for(int i = 0; i < ratings.size(); i++) {
if(diff[i] == true) count++;
else count = 1;
result += count;
}
return result;
}
};
正確思路代碼
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> Candy(ratings.size(), 1);
for(int i = 1; i < ratings.size(); i++) {
if(ratings[i] > ratings[i - 1]) Candy[i] = Candy[i - 1] + 1;
}
for(int i = ratings.size() - 2; i >= 0 ; i--) {
if(ratings[i] > ratings[i + 1]) Candy[i] = max(Candy[i + 1] + 1, Candy[i]);
}
int result = 0;
for(int i = 0; i < Candy.size(); i++) {
result += Candy[i];
}
return result;
}
};
總結
自己实现过程中遇到哪些困难
今天遇到困難點的主要是加油站,加油站的確沒有想到區段和如果為負的則由下一個位置當作起始點,另外就是分發糖果,一開始的確掉到坑裡,但看完題解後,發現思路也的確不難,自己對於貪心算法真的比較矇,需要多練習,建立不同思考題目的觀察力,解貪心才會比較得心應手。
今日收获,记录一下自己的学习时长
今日大概學習了3hr,主要都在思考,實際上的代碼複雜程度反而沒有那麼高,在貪心真的思路比較重要。
相關資料
● 今日学习的文章链接和视频链接
1005.K次取反后最大化的数组和
https://programmercarl.com/1005.K次取反后最大化的数组和.html
134. 加油站
https://programmercarl.com/0134.加油站.html
135. 分发糖果