Candy

又是一道贪心题目,试了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;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值