今天第一次做hard题,并且做了两个,不出意外全都没能拿下。脑子里第一想法要么全都是暴力求解,要么即使知道是greedy,也想不出来具体怎么贪心。
1.leetcode135
第一种方法:Two-pass(也是我看到hint后采取的方法)
class Solution {
public:
int candy(vector<int>& ratings) {
int n=ratings.size();
vector<int> candys(n,1);
int sum=0;
for(int i=1;i<n;i++){
if(ratings[i]>ratings[i-1]){
candys[i]=candys[i-1]+1;
}
}
for(int i=n-2;i>-1;i--){
if(ratings[i]>ratings[i+1]){
candys[i]=max(candys[i],candys[i+1]+1);
}
}
for(int i=0;i<n;i++){
sum+=candys[i];
}
return sum;
}
};
非常直观。第一遍pass确保所有的人都比左边拿到的candy多。第二遍pass确保所有人都比右边拿到的candy多。并且都是greedy的。
第二种方法:One-Pass
class Solution:
def candy(self, ratings: List[int]) -> int:
if not ratings:
return 0
ret, up, down, peak = 1, 0, 0, 0
for prev, curr in zip(ratings[:-1], ratings[1:]):
if prev < curr:
up, down, peak = up + 1, 0, up + 1
ret += 1 + up
elif prev == curr:
up = down = peak = 0
ret += 1
else:
up, down = 0, down + 1
ret += 1 + down - int(peak >= down)
return ret
这个想法太amazing啦!up和down分别记录向上和向下走了多少步。每向上走一步,每次candy总数增加的数量就多1个(第一次增加1,第二次增加2这样)。如果是平的,根据规则,可以比前者少,那直接贪心,就给它设置成1,所以直加1。如果向下走,可以根据贪心,每次都把最新的那个设置成1,那为什么要加上down呢?比如连续的是3,2,1:遇到2的时候,3是1,这时需要把3加1,即3是2,2是1;遇到1的时候,需要把2加1,此时3又要加1,所以总共加2。为什么要减去int(peak>=down)呢?因为如果peak已经比down的总数都要大,此时不需要再加1了。
2.leetcode42
第一种方法:遍历两遍,找到左边最大和右边最大。
class Solution {
public:
int trap(vector<int>& height) {
int n=height.size();
vector<int> left(n,0);
vector<int> right(n,0);
int max_left=0;
int max_right=0;
for(int i=1;i<n;i++){
max_left=max(max_left,height[i-1]);
left[i]=max_left;
}
for(int i=n-2;i>-1;i--){
max_right=max(max_right,height[i+1]);
right[i]=max_right;
}
int water=0;
for(int i=1;i<n-1;i++){
water+=max(min(left[i],right[i])-height[i],0);
}
return water;
}
};
很直观,没什么可说的(虽然让我想一开始也想不出来)。
第二种方法:two pointers
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int lmax = height[0];
int rmax = height[n-1];
int lpos = 1;
int rpos = n-2;
int water = 0;
while(lpos <= rpos)
{
if(height[lpos] >= lmax)
{
lmax = height[lpos];
lpos++;
}
else if(height[rpos] >= rmax)
{
rmax = height[rpos];
rpos--;
}
else if(lmax <= rmax)
{
water += lmax - height[lpos];
lpos++;
}
else
{
water += rmax - height[rpos];
rpos--;
}
}
return water;
}
};
这个方法比较抽象,需要细细品味。
首先四个if为什么是用这种关系嵌套的?为什么不能调换?首先,如果出现height[lpos] >= lmax或者height[rpos] >= rmax,这说明这个位置必定存不住水,直接跳过并更新最大值即可。其次,如果不满足上述两个关系,则说明lpos处或者rpos处能存水了。此时就比lmax和rmax谁小谁大。如果lmax小,那么根据木桶效应,lpos处最大存水量能确定了。为什么rpos处没法确定?因为后续lmax有可能继续更新变大!rpos处同理。
3.leetcode13
这一题确实是easy的水平。
class Solution {
public:
int romanToInt(string s) {
int n=s.size();
int max_=0;
int cur=0;
int INT=0;
for(int i=n-1;i>=0;i--){
if(s[i]=='I'){
cur=1;
}
else if(s[i]=='V'){
cur=5;
}
else if(s[i]=='X'){
cur=10;
}
else if(s[i]=='L'){
cur=50;
}
else if(s[i]=='C'){
cur=100;
}
else if(s[i]=='D'){
cur=500;
}
else if(s[i]=='M'){
cur=1000;
}
if(cur>=max_){
INT+=cur;
max_=cur;
}else{
INT-=cur;
}
}
return INT;
}
};
一开始我为了避免比如8=IIX的情况出现,还从后往前算,但后来发现根本没有IIX这种,只有IX这种,所以从前往后算也行。