开灯问题
内存限制:256 MB 时间限制:1000 ms
题目描述
房间中现有 n 枚灯泡,编号从 1 ~ n,自左向右排成一排。
最初0时刻时,所有的灯都是关着的。
在 k 时刻(1 ≤ k ≤ n),我们打开编号为 light[k] 这个灯。
灯的颜色要想变成蓝色,就必须同时满足下面两个条件:
- 灯处于打开状态。
- 排在它左侧的所有灯也都处于打开状态。
请返回能够让所有开着的灯都变成蓝色的时刻数目 。
输出格式
输出只有一个整数,表示能够让所有开着的灯都变成蓝色的时刻数目 。
样例
样例输入
复制5
2 1 3 5 4
样例输出
复制3
样例解释 1
0表示关灯状态,1表示开灯状态,2表示蓝灯状态,过程如下:
复制1时刻:0 1 0 0 0
2时刻:2 2 0 0 0 √
3时刻:2 2 2 0 0 √
4时刻:2 2 2 0 1
5时刻:2 2 2 2 2 √
所有开着的灯都变蓝的时刻分别是2,3和5。
数据范围
-
输入的数字都是整数。
-
n与light序列元素数量相同。
-
1 ≤ k ≤ n。
-
1 ≤ n ≤ 5*10^4。
-
light是[1, 2, 3, …, n]的一个排列。
解法1:模拟
思路
1、定义一个数组light用于存放每个灯的状态(0表示关灯状态,1表示开灯状态,2表示蓝灯状态)
2、从头开始遍历数组,如果light[j]是开灯状态,也就是light[j] != 0,那么它就可以变为蓝灯状态,否则就直接跳出循环
原理:
如果要light[x]变为蓝灯状态,light[1]到light[x]都要是开灯状态,所以就可以直接从头开始遍历数组,直到light[i]为关灯状态
3、接着遍历数组看后面的灯是否都为蓝灯或关灯状态(注意:这里不能从头遍历,否则就会时间超限),如果是则ans++
4、最后输出ans即可
代码
#include <bits/stdc++.h>
using namespace std;
#define MAX 50005
int light[MAX];//1、用于存放每个灯的状态(0表示关灯状态,1表示开灯状态,2表示蓝灯状态)
int main(){
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int k, j;
scanf("%d", &k);
light[k] = 1;
for (j = 1; j <= n; j++){2、从头开始遍历数组
if (light[j] != 0) {//如果light[j]是开灯状态,也就是light[j] != 0
light[j] = 2;//那么它就可以变为蓝灯状态
} else {
break;//否则就直接跳出循环
}
}
bool flag = false;
for (; j <= n; j++) {//3、接着遍历数组看后面的灯是否都为蓝灯或关灯状态
if (light[j] == 1) {
flag = true;
break;
}
}
if (!flag) {//如果是则ans++
ans++;
}
}
printf("%d", ans);
return 0;
}
解法2:比较最右边亮灯与一共亮灯数
思路
思路与解法1大体相似。
既然只要是从左开始且连续的亮灯,就都可以变为蓝灯状态,那么我们就只需要保存最右侧的亮灯,以及一共亮灯的数量,如果他们相等,那么所有的亮灯都在左侧且连续,即都为蓝灯状态,ans++。
这样一来,我们就只需要在在输入的时候持续刷新最右侧亮灯的位置(maxn),并让maxn与一共亮灯的数量进行比较,又因为每输入一个数就会亮一盏灯,所有一共亮灯的数量即是循环变量 i ,如果maxn等于i,则说明所有亮灯均为蓝灯,ans++,最后输出ans即可。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, maxn = -1, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int k;
scanf("%d", &k);
maxn = max(maxn, k);//持续刷新最右侧亮灯的位置
if (maxn == i) {
ans++;//如果最大值与一共亮灯数相等,则说明亮的灯都为蓝灯状态
}
}
printf("%d",ans);
return 0;
}
代码只是辅助,要了解思路。
革命靠自觉,尽量不要复制啊啊啊啊啊
最后,留下你的大拇指