输入样例:
3
3 10
4 6 7
2 12
1 10
2 7
3 4
输出样例:
8
9
6
前缀和 + 模拟 + 贪心
模拟在每个时间段插入点
-
首先计算关灯和开灯时间的前缀和
-
那么奇数时间段一定是开灯,偶数段一定是关灯
-
当在每一个时间段插入时,贪心选择当前时间段插入后关灯的时间一定是1,开灯的时间为x-1
-
在插入时间点后,一定只影响当前时间段后面的所有时间段,且他们的开关灯属性交换,原来开灯变为关灯,关灯变为开灯
-
为了快速计算插入后,后面时间段的所有开灯的时间段,即前缀和来计算即可
-
注意:若时间段长度为1,则跳过,插入不了
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
int n,m;
cin>>n>>m;
int arr[n];
int time[n+1]; // 各个时间段
for(int i = 0;i<n;i++){
cin>>arr[i];
}
time[0] = arr[0];
for(int i =1;i<n;i++) time[i] = arr[i]-arr[i-1];
time[n] = m - arr[n-1];
int sum[n+1]; // 前缀和,偶数记录light,奇数记录dark
sum[0] = time[0];
sum[1] = time[1];
int ans = 0; // 答案
int sum_dark = time[1],sum_light = time[0];
for(int i = 2;i<=n;i++){
sum[i] = time[i] + sum[i-2];
if(i%2 == 0) sum_light = sum[i];
else sum_dark = sum[i];
}
ans = sum_light; // 所有不插入时就是亮灯的时间和
// 遍历每一个位置插入
if(time[0]>1) ans = max(ans,time[0]-1+sum_dark); // i=0单独算,因为sum[i-1]超出界限
for(int i = 1;i<=n;i++){
if(time[i] == 1) continue; // 无法插入
if(i%2){
// 当前是dark区域
// 插入后,此区间的dark变为1
int temp = sum[i-1] + time[i]-1 + sum_dark - sum[i];
ans = max(ans,temp);
}else{
// 当前是light区域
// 插入后,此区间的dark为1
int temp = sum[i-1]-1 + sum_dark - sum[i-1];
}
}
cout<<ans<<endl;
}
return 0;
}