又是一道贪心题目,试了dfs,dp,divide-and-conquer多种做法,实在做不出来,看了tag提示要用贪心,又琢磨了半天终于算是做出来了。
dfs和dp会在大数据时超时,divide-and-conquer在第21个测试用例时答案错了,但我暂时没看出来代码逻辑哪里有问题,我会把分而治之的代码贴在后边,欢迎同胞指正。
先贴AC的贪心解法,关键是要认清楚两点:
1.由于题目没有对当相邻的小孩有相同的rating时做限制,所以每当遇到连续的相同rating时,可以当做新的开始来做。(比如有连续的5,5,5,这时第一个5是根据它前面的约束来取值,但后两个5没有任何约束,均区最小值1,然后从最后一个5开始,又可以看做是新的开始)。这样一来就把原区间分成了若干个区间,每个区间里都是绝对的rating上升与(或)下降的组合,不会再出现相同rating的干扰
2.其次要追踪当前处理的究竟是上升区段还是下降区段,在单调上升区段或者单调下降区段,都后一个取值只要比前一个取值大1即可保证最优取值(遇到单调下降要能在逻辑上转成“倒着看的单调上升”),当从上升转成下降或从下降转成上升时,转折点的取值会与之前的取值发生变化。从下降转上升,很简单,重置取值即可。从上升转下降比较复杂,因为下降的深度可能会比之前上升的高度要大,这样上升最高点的取值就会发生变化,最高点的要取上升区间高度和下降区间深度的最大值,这样才不会把下降区段”压入负数范围“
代码如下
class Solution {
public:
int nextVal(int pos,const vector<int>& ratings)
{
int res=pos;
while((res<ratings.size())&&(ratings[res]==ratings[pos]))
{
res++;
}
return res;
}
int candy(vector<int> &ratings) {
int n=ratings.size();
if(n==0)
{
return 0;
}
if(n==1)
{
return 1;
}
//continuous same ratings means a new start,because there is no constraint push on continuous same ratings
int res=0;
int prepos=-1;
int pos=0;
int acc=1;
bool ascent=true;
while(pos<n)
{
//handle current pos
if(acc==1)
{
res+=acc;
acc++;
}
else if(acc==2)
{
if(ratings[pos]>ratings[prepos])
{
ascent=true;
}
else
{
ascent=false;
}
res+=acc;
acc++;
}
else
{
if((ratings[pos]>ratings[prepos])&&ascent)
{
res+=acc;
acc++;
}
else if((ratings[pos]<ratings[prepos])&&!ascent)
{
res+=acc;
acc++;
}
else
{
//current pos changed the change trend from downside to upside
if((ratings[pos]>ratings[prepos])&&!ascent)
{
acc=2;
res+=acc;
acc++;
ascent=true;
}
else
{
//from upside to downside
//this need to be pay attention,because we do not know how deep this downside will go on
//we know preceding upside goes up to acc,so if we dive less than it, we need not to modify
//prepos's value, otherwise we need to update prepos's value to the deepest dive height
int tmppos=pos+1;
while((tmppos<n)&&(ratings[tmppos]<ratings[tmppos-1]))
{
tmppos++;
}
int downheight=tmppos-pos;
int upheight=acc-2;
if(downheight>upheight)
{
res-=(acc-1);
res+=(1+downheight+1)*(downheight+1)/2;
}
else
{
res+=(1+downheight)*(downheight)/2;
}
ascent=false;
pos=tmppos-1;
}
}
}
//find next pos
int newPos=nextVal(pos,ratings);
if((newPos-pos)>1)
{
//continuous same ratings, meaning a new start
res+=(newPos-pos-1);//because pos is already considered,so minus one
acc=2;//because the start of the new begining is alreay considered in the above equation
}
else
{
//there is no continuous same ratings,so no particular handle
}
prepos=pos;
pos=newPos;
}
return res;
}
};
下面是我的divide-and-conquer的代码,测试用例20出错,希望大神们能指出逻辑上的错误
class Solution {
public:
void incVector(int inc,int start,int end,int step,vector<int>& input,const vector<int>& ratings)
{
int i=start;
while(((i+step)!=end)&&(input[i]<input[i+step])&&((input[i]+inc)>=input[i+step])&&(ratings[i]!=ratings[i+step]))
{
input[i]+=inc;
i+=step;
}
input[i]+=inc;
return;
}
void divideAndConquer(int start,int end,vector<int>& res,const vector<int>& ratings)
{
if(end-start==1)
{
res[start]=1;
return;
}
int mid=start+(end-start)/2;
if(start<mid)
{
divideAndConquer(start,mid,res,ratings);
}
if(mid<end)
{
divideAndConquer(mid,end,res,ratings);
}
if(start==mid)
{
return ;
}
if(mid==end)
{
return ;
}
if(ratings[mid-1]==ratings[mid])
{
return;
}
else if(ratings[mid-1]<ratings[mid])
{
if(res[mid-1]<res[mid])
{
return;
}
else
{
//need to incrment right
int inc=res[mid-1]-res[mid]+1;
incVector(inc,mid,end,1,res,ratings);
return;
}
}
else
{
//ratings[mid-1]>ratings[mid]
if(res[mid-1]>res[mid])
{
return;
}
else
{
//need to increment left
int inc=res[mid]-res[mid-1]+1;
incVector(inc,mid-1,start-1,-1,res,ratings);
return;
}
}
}
int candy(vector<int> &ratings) {
int n=ratings.size();
if(n==0)
{
return 0;
}
vector<int> res(n,1);
divideAndConquer(0,n,res,ratings);
int acc=0;
for(int i=0;i<res.size();++i)
{
acc+=res[i];
}
/*
cout<<"candies"<<endl;
for(int i=0;i<res.size();++i)
{
cout<<res[i]<<" ";
}
cout<<endl;
*/
return acc;
}
};