2 条题解
-
4
额外拨动开关,则该时间点之后所有原来的亮灯时间变为熄灯时间,熄灯时间变为亮灯时间,即交换了亮熄灯时间。
所以若是某次拨动开关的时间点之后的总熄灯时间大于总亮灯时间,我们可以在该时间点之后额外添加一个拨动开关的时间点,后边的总亮灯时间和总灭灯时间就会交换,这样总体亮灯时间就会变长
所以我们只需要从数组的末尾开始遍历,找哪个拨动开关的时间点a[i]后边的总熄灯时间-总亮灯时间为正值且差值最大,就在这个时间点a[i]后边相邻的时间点额外拨动一次开关交换后边的熄灯和亮灯时间,这样最长亮灯时间就=该a[i]点前边的总亮灯时间+该a[i]点之后的总灭灯时间-1(相邻两个时间点之间的灯的状态并未改变)
当然,若是每次拨动开关的点位之后的总熄灯时间-总亮灯时间为负值,则不用额外添加拨动开关。
核心代码:
//输入并计算存取每个点前边总开灯时间 for(i=1;i<=n;i++) { scanf("%d",&x[i]); if(i%2==1)k+=x[i]-x[i-1]; y[i]=k; } //计算并存取每个点后边总关灯时间 x[n+1]=m; int g=0;//总关灯时间 k=0; int cha;//每个点后边关灯时间-开灯时间的差值 int max=0;//最大差值 int maxn;//最大差值点 for(i=n;i>=0;i--) { if(i%2==0) k+=x[i+1]-x[i]; else g+=x[i+1]-x[i]; z[i]=g; cha=g-k; if(cha>max) { max=cha; maxn=i; } }
-
2
这题虽然题目写的是二分,但是实际正解和二分没啥关系,是前缀和的预处理问题,想明白开关灯的状态就行了。
画图理解如下:
操作是n段内每一段都加入操作判断一下。
核心代码:
// s1 奇数前缀和, s2偶数前缀和(奇数代表d区间,偶数代表L区间) for(int i =1; i <= n ; i++) cin>>a[i]; a[++n] = m; //把m看成是一个时刻,把它放到a[n+1]里 s1[n] = s2[n] = 0; for(int i = n-1 ; i >= 0 ; i--) { s1[i] = s1[i+1]; s2[i] = s2[i+1]; if(i % 2 == 0 ) s1[i] += a[i + 1] - a[i]; else s2[i] += a[i + 1] - a[i]; } int ans = s1[0]; //一次都不按的灯亮时间总长度,注意此处维护的是后缀和 所以下标是0 for(int i = 0 ; i < n ; i++) // 从前往后枚举每个区间怎么按 { int t = a[i + 1] - a[i]; // 求一下当前区间的长度 if( t == 1) continue; //如果当前区间亮灯长度 已经是1了 ans = max(ans, s1[0] - s1[i] + s2[i+1] + t -1); // s1[0] - s1[i] 是前面的奇数区间 }
- 1
信息
- ID
- 816
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 64
- 已通过
- 10
- 上传者