目录
1.力扣-367
思路
通过之前浮点数二分例题:数的三次方根 的基础上,我们就很容易能想到这种开方的题思路实质是二分的。(额,突然发现之前写过求x平方根的题解,算啦再多解释一下)
虽然用浮点数二分写这道题更直观更准确,但是这道题要求是定义为整数,所以我们要按要求来。首先要先找到二次方根,相较浮点数二分,我们要多考虑一点性质的划分条件,这道题进行二分的性质应该是,找到第一个 平方之后的数 小于所给的数(这里是因为,向下取整的原因[下面会详细解释],我们尽量往小的数上找,即尽量向左找)。
接着在考虑如何判断是否是完全平方数,如果一个数不是完全平方数,开方之后应该是有小数,但是我们既然是整数二分,那么所得到的数肯定是一个整数,那到底应该怎么判断是不是呢?这时候我们想到由于整型除法的取整方式是向下取等,也就是说2.多的会得出2,这说明如果这个数不是完全平方数,那么我们在循环结束得到取整后的二次方根记为 l (因为循环结束的时候 l=r=mid),再多写一个if语句,判断刚刚得到的 l*l 和 x 的大小关系,如果< 则说明不是完全平方数,那么返回false ,否则返回true。想一下可以理解哈。
思路明白了哈。
数据范围
num 的最大值是2^31-1,那么开根号,最大应该是46340。
r 的初始值为 num,所以它的最大值为 2147483647。但是,在二分查找过程中,r 的值会不断减小,最终不会超过 46340。但是当 r 的值大于 46340 时,mid 的值也会大于 46340。所以mid类型应该定义成 long long类型。后面的1ll 就表示把这个表达式转换成long long类型
int mid = (l + r + 1ll) / 2;
同时在后面,mid*mid 的结果将超过最大值,导致溢出。所以在while循环中的if判断条件写成除法形式避免溢出。
但是最终判断是否时完全平方数的时候,if 条件可以写成乘法形式,是因为在二分查找过程中,当 r 的值大于 46340 时,总会执行 r=mid-1; 这一行代码,使得 r 的值不断减小。最终,r 的值不会超过 46340。由于 l 的值始终小于等于 r,所以 l*l 不会溢出。
二分代码如下
#include <iostream>
using namespace std;
class Solution
{
public:
bool isPerfectSquare(int num)
{
int l = 0, r = num;
while (l < r)
{
int mid = (l + r + 1ll) / 2;
if (mid <= num / mid)
l = mid;
else
r = mid - 1;
}
// cout<<l<<endl;
if (l * l < num)
return false;
else
return true;
/* 如果想用除法的书写形式,注意由于向下取整,我们必须再多判断一下余数是不是0 才能确定是不是完全平方数
if(l==num/l && num%l==0)return true;
else return false;
*/
}
};
int main()
{
int x;
cin >> x;
Solution s;
bool ans = s.isPerfectSquare(x);
cout << ans << endl;
return 0;
}
ok这道题先到这。
直呼优雅!
奥对了在讨论区看到个巨牛的数学思路。
4=1+3 9=1+3+5 16=1+3+5+7。可以看出完全平方数都是由奇数相加而得,那么就可以使用一个while循环,不断减去一个从1开始不断增大的奇数,若最终减成了0,说明是完全平方数,否则,不是。
其原理就是:(n+1)^2-n^2=2n+1
代码如下
#include <iostream>
using namespace std;
class Solution
{
public:
bool isPerfectSquare(int x)
{
int num = 1;
while(x > 0)
{
x -= num;
num += 2;
}
return x == 0;
}
};
int main()
{
int x;
cin >> x;
Solution s;
bool ans = s.isPerfectSquare(x);
cout << ans << endl;
return 0;
}
我直接惊呼!!太优雅了!!
2.洛谷-2249
这道题思路也不难,题中要求输出该数字在序列中第一次出现的编号,由此我们可以确定二分的性质是,向左找,找到第一个>=目标元素的值。
写了那么多次了,模板就不再解释了哈。
直接上代码
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N];
int n,m,q;
int find(int q)
{
int l=1,r=n;
while(l<r)
{
int mid=l+(r-l)/2;
if(a[mid]>=q)r=mid;
else l=mid+1;
}
//找到就返回下标,没找到返回-1
if(a[l]==q)return l;
else return -1;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
while(m--)
{
cin>>q;
int ans=find(q);
cout<<ans<<" ";
}
return 0;
}
(稍微提一嘴,我是在vscode上写代码,之后复制到洛谷上的,这样是过不了的,tab键出现问题,嗯,有大佬清楚这个问题的话求指点!!)(上面这个没问题!)
哇哦,第一次题解写这么短(hhhh服了我了) 说明什么?practice makes perfect!!(好中二hhhh)
ok啦,这次先写两道题,二分这部分还有两个习题。留到下一篇写把。感觉有个题比较难懂。
有问题欢迎指出哦,非常感谢!!
也欢迎交流和建议奥。