今天leetcode的每日一题给到了1954——收集足够苹果的最小花园周长,难度中等。
第一眼看到这道题的时候,我就想到了可以“以小解大”,就是将整个花园分成四等份,每个象限为一份,算出的结果只需要乘以4就可以了,但是随着想法的深入我发现,竟然都已经把他化得这么小了,为什么不直接一条路走到黑,再分成两个三角形呢?就是如下的三角形。,由于题目的特殊性,对角线上的数一定是偶数,于是我把对角线上的树分成两部分,但是x轴上的数没那么好运,并不全是偶数,但是无妨,我还是直接把它“劈成两半”。到此纵观全局来看,所有将树分开的行为,都是为了最后乘以8,才是完美等于原先的数量的。同时,我们选的位置坐标值都是正的,于是就不用考虑绝对值的情。以后在遇到这种题时,都可以采用这种分割求解的办法,减少运算量同时增大成功率。
又由于分割的三角形是等腰直角三角形,我定义一个x,y,就可以把所有坐标表示出来,于是现在的考虑就是,我要求出比neededApples大的那个x,于是考虑双重循环,由于数值间数据类型不同,所以为了避免数据丢失,我都将其强制转化成double类型。但是这么简单的代码还是超时了,因为是双重循环,该算法的时间复杂度为O(n²),对于很大的数据来说,循环两次一定会超时,但是不可否认的是,这个算法确实是正确的。于是我便重新考虑能不减少一层循环呢?
long long minimumPerimeter(long long neededApples) {
long long x,y;
double sum=0;
for ( x=0;sum<(double)neededApples/8;x++)
{
for ( y=0;y<=x && sum<(double)neededApples/8;y++)
{
if (x==y || y==0)
{
sum+=(double)(x+y)/2;
}
else
{
sum+=(double)x+(double)y;
}
}
}
return (x-1)*8;
}
回想一下题目的特殊,这个题目我将其分割成了八份,这八份都是完全相等的等腰直角三角形,基于这个特性,我在想能不能通过数学公式来求解这个问题。回到最初,我们把不属于三角形的部分删去了,导致我看不出来这个规律,现在我又把它加回去,观察是不是有规律。
相信聪明的你也看出来了,这里的每一列的苹果的和不就是满足等差数列的公式吗?是的,那让我来找出这个等差数列的基本元素。
①对于一个首项a1=x,它的末项an=2*x;
②对于每一组数据,它的公差d=1;
③对于每一组数据,它的项数为n+1;
结合上述结果,每一个以x为首项的数列的和为sn=n*a1+n(n-1)*d/2,算出来是sn=(3*x*x+3*x)/2,于是每一组数据的和就求完了,但别忘了,刚刚加上去的不属于三角形内的数要减去,也就是减去x/2+x,于是真正的和为Sn=3*x*x/2,下面是代码的实现。
long long minimumPerimeter(long long neededApples) {
long long x;
double sum=0;
for ( x=0;sum<(double)neededApples/8;x++)
{
sum+=(double)x*(double)x*3/2;
}
return (x-1)*8;
}
这个代码过了,我以为这个代码够简单了,因为只用了一层循环,时间复杂度为O(n),但是只打败了20%的人,后面我看题解,发现居然还有两行代码能够完成的大佬。通过刷算法题我感受到了自己不会的还有很多,还是需要认真学。
如有需要改进的地方,欢迎指出。感谢您看到这里!