A
一道轻松的签到题,不多加赘述了。熟悉语法就可以完成。
B
要点:
1.由于奇数偶数相加的规律,只能将所有数字变为奇数(在初始数组不全是偶数的情况下)
2.考虑到操作限制,有一种情况下,可以将奇数和最大的偶数相加得到最大奇数,此时操作数会比简单情况多1.
C 同时亮灯
题目描述
有n盏灯,其中第i盏灯在ai的时刻被打开,随后开始以k时间为周期的开灭循环。
(设第x秒开灯,那么亮灯时间为x——x+k-1;x+2k——x+2k+k-1……)
求所有灯同时打开的时刻(最小值)。
思路
假设最终答案为第s秒。
1.s>=max(a);必须先激活灯后,才有同时开灯时刻;
2.开灯时间的循环是2k,因此可以把所有时间放置到2k这段时间段上,那么题目就化简成为,找到所有长度为k的片段重合部分,并通过对max(a)的比对,找到实际最小的时刻。
收获
在负数求模时,c++中得到的答案一定是负数,需要按照实际要求加一个周期。
在求重合片段时有两种方式:最容易想到的是利用差分数组(但要注意循环的处理);另外一种思路是本题的官方题解给出的;具有一定的限制,但是操作更简单,重点讲题解给出的方法。
由于所有的片段长度是一样的(限制),找重叠片段这一问题可以反过来想。假设在r-k+1——r这个区间,有所有的ai点(所有片段的开头都在答案片段内)。那么便可以认为,所有的片段都包含了r这一点。因此在找r的时候只需要一个for循环便利即可,也是要注意循环处理。
ac代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int main()
{
int n,k,t,a;
int d[2*N];
cin>>t;
while(t--){
cin>>n>>k;
a=-1;
memset(d,0,sizeof(d));
for(int i=0;i<n;i++){
int x;
cin>>x;
if(x>a) a=x;
d[x%(2*k)]++;
}
int l,r,cnt=0;
for(r=0;r<k-1;r++){
cnt+=d[r];
}
int ans=INT_MAX;
for(l=0,r=k-1;l<2*k;l++,r++){
if(r>=2*k) r-=2*k;
cnt+=d[r];
if(cnt==n){
int temp=(r-a)%(2*k);
if(temp<0) temp+=2*k;
ans=min(ans,temp+a);
}
cnt-=d[l];
}
if(ans==INT_MAX){
ans=-1;
}
cout<<ans<<endl;
}
}
D Med-imize
题目描述
有一个长度为n的数组,每次操作去掉连续的k个数,直到数组元素个数<=k。求所有去除方式中,剩下的数组元素的中位数最大的答案是多少?
思路
1.有没有什么办法可以在不排序的情况下找到中位数?
由于中位数只在意大小比较,所以通常遇到中位数的问题,会把数组内的元素精简为-1,0,1。分别对应小于,等于,大于。(当然,本题没有那么简单)
可以利用二分答案找到中位数。
2.如何得到剩下的数组中的数字?
枚举所有可能的新数组明显是不可能的,所以我们在选择或者说删除操作的时候,应该考虑到的是我们需要留下什么样的数字。
不是先找到数字后计算中位数,而是根据最大中位数的需求来确定应该怎么做删除操作。可能这也是为什么这题要用到二分答案的原因(二分答案的本质就是代入法试答案)
这里涉及到一个有关循环的技巧:
我们假设,k=4,n=10;那么对index做%4处理,有:
0 1 2 3 0 1 2 3 0 1;
简单的尝试可以发现规律,每次减少k个数时,减少的数中总是包含0 1 2 3;剩下的数字一定是n%k个,(此时cnt=2,数字为 0 1)
我们把他整理成二维
0 1 2 3
0 1 2 3
0 1
此时,这些数字对应的是index,那么他的value是什么呢? value是上面提到的-1,0,1
这个问题就可以精简为,找到包含 0 1两个索引的最大总和是多少(有限制条件,0必须在1前面)。
用dp的技巧选择最优的答案。并对最优答案做判断,此时假设的中位数是否符合要求。二分答案
收获
利用二分答案查找中位数。
中位数的常规处理(精简为0,-1,1)
根据作题思路来说,这题需要利用:需要求解的中位数来进行删除操作。有点像是用未知数的方式解应用题,所以这个x(中位数)具体是多少是不知道的,这或许也是二分答案的一种表现形式。
在做删除固定k个数字的操作时,考虑到利用求模的技巧转换成二位的数组,并用dp的技巧选择最优的答案。
这里的dp,有几点限制:1. 0索引一定要在1索引的上面或者同行(每次必须删除k个,没有循环);2.所有的数字里面,0,1等等都只能选择一次。
所以在设计状态转移方程的时候要注意0号位置的最优选择,以及其他位置的计算和对比。
ac代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int t,n,k;
int a[N],d[N];
bool check(int mid){
int i;
for(i=0;i<n;i++){
if(a[i]>=mid){
d[i]=1;
}
else{
d[i]=-1;
}
//cout<<d[i]<<" ";
}
int dp[N];
dp[0]=d[0];
for(i=1;i<n;i++){
if(i%k==0){
dp[i]=max(dp[i-k],d[i]);
}
else{
if(i>k){
dp[i]=max(dp[i-k],dp[i-1]+d[i]);
}
else{
dp[i]=dp[i-1]+d[i];
}
}
}
if(dp[n-1]>0) return true;
return false;
}
int main()
{
cin>>t;
while(t--){
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i];
}
int l=0,r=1e9+5,mid;
while(l<=r){
mid=(l+r)/2;
//cout<<endl<<mid<<" ";
if(check(mid)){//等于时l变化
l=mid+1;
}
else{
r=mid-1;
}
}
cout<<r<<endl;
}
}