题目描述:
初始时有 n 个灯泡关闭。
第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。
第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。
找出 n 轮后有多少个亮着的灯泡。
示例 1:
输入:n = 3
输出:1
解释:
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭].
你应该返回 1,因为只有一个灯泡还亮着。
示例 2:
输入:n = 0
输出:0
示例 3:
输入:n = 1
输出:1
提示:
0 <= n <= 109
方法1:
主要思路:
(1)对题目进行分析,可以发现,最终的亮着的灯对应的索引分解后,其因数的数量为奇数个,这样,经过一些列的开关之后,依旧是开着的状态;
(2)然后直观的想,就是对每个索引进行分解,统计其因数的数量,若是奇数个,则灯是亮的,否则,是关闭的;
(3)该方法超时;
class Solution {
public:
int bulbSwitch(int n) {
//处理特殊的情形
if(n==0){
return 0;
}
if(n<=3){
return 1;
}
//统计亮着的灯的数量
int res=1;
for(int i=3;i<=n;++i){
int end=i/2;
int cur_count=0;
//对当前灯对应的索引进行因数分解,找出因数的数量
for(int j=2;j<=end;++j){
if(i%j==0){
++cur_count;
}
}
//判断数量的奇偶性
if(cur_count%2==1){
++res;
}
}
return res;
}
};
方法2:
主要思路:
(1)方法1超时,想着对每个索引计算因数分解,耗时,重复,故将数字从小到大进行乘,获得对应的数字,在对应的数字上加上因数数量,最后再判断因数的总的数量的奇偶性;
(2)可以计算更大的数,但同样超时;
class Solution {
public:
int bulbSwitch(int n) {
//计算特殊的情形
if(n==0){
return 0;
}
if(n<=3){
return 1;
}
//统计各个索引的因数分解的数量
vector<int> nums(n+1,2);
int end=sqrt(n);//终止位置
//逐渐的计算各个可能的数字
for(long long i=2;i<=end;++i){
for(long long j=i;j<=end;++j){
if(i*j>n){//后面的组合不在范围内
break;
}
if(i==j){//两个数相同,则加一个因数
++nums[i*j];
}
else {//两个数不同,则加两个数
nums[i*j]+=2;
}
}
}
//根据各个索引的因数数量的奇偶性,统计总的灯的数量
int res=1;
for(int i=3;i<=n;++i){
if(nums[i]%2==1){
++res;
}
}
return res;
}
};
方法3:
主要思路:
(1)方法2同样超时,但在方法2中进行因数统计时,发现,每个数字的因数增长时,除了是平方和的情形,都是一次加上两个数,既一直能保持偶数的情形,只有在遇到平方和的情形时,才可能变成奇数,而对每个数字的因数分解,其实最多只可能遇到一次平方,故可以只有平方和的数字,才可能获得因数数量是奇数的情形,只需要统计平方和的情形即可;
class Solution {
public:
int bulbSwitch(int n) {
int res=1;//初始值
//统计范围内的平方和
while(res*res<=n){
++res;
}
//减去越急的一个
return res-1;
}
};