1286A
Garland
题目出处
题目大意
给定一长度为 \(n\) 的序列,其中有一些已经填好了。要求用还没填上 \(1 \sim n\) 的数字填入序列,从而最小化序列中相邻但奇偶性不同的数对数量。
知识点、思路
贪心
具体想法、推导
显然我们可以只关注每个数的奇偶性,分别处理出可填的奇偶数的数量。
首先,把没有填好的连续的位置看成一个区间是比较自然的想法,对于一个区间,我们可以思考最好的填入方法。
我们可以把区间分成三类,用点表示没有填入的数,\(1/0\)表示区间边界的值的奇偶性:
第一类是形如 \(...... 1\) 和 $ 0 ......$ 的区间,即只有一侧有边界,显然都填入与边界相同奇偶性的数可以使得答案减 \(1\)
第二类是形如 \(1 ......1\) 和 \(0 ......0\) 的区间,即区间两侧都有边界,且两侧边界奇偶性相同。显然都填入与边界相同奇偶性的数可以使得答案减 \(2\)
第三类是形如 \(1 ......0\) 和 \(0 ......1\) 的区间,即区间两侧都有边界,且两侧边界奇偶性不同。此时可以发现,要填入一个数,最好的方法就是靠近与它奇偶性相同的位置,填好之后形如 $1 1 1 1 1 0 0 0 $ ,无论要填入的数奇偶性如何,对于此类区间,始终使得答案加 \(1\)
因此,为了使答案值最小化,我们要先预处理出三类区间,并优先填入第二类区间,无法填入第二类区间后,再考虑填入第一类区间。关于同类区间的填入,显然是从长度从小到大的顺序可以使得填入的区间数最多,因此要对区间进行排序。并且可以发现,填入第一、二类区间时,填入奇数和填入偶数是独立开来的,可以分开计算。
示例代码
void solve()
{
cin>>n;
int cnt1=n/2+(n%2),cnt2=n/2;
for (int i=1;i<=n;i++){
cin>>a[i];
if (a[i]==0) {
a[i]=-1;
continue;
}
a[i]%=2;
if (a[i]==1) cnt1--;
else cnt2--;
}
if (n==1) {
cout<<0<<endl;
return;
}
if (cnt1+cnt2==n){
cout<<1<<endl;
return;
}
priority_queue<int,vector<int>,greater<int>> q1,q2; //小根堆
int st=1,ed=n;
while (st<=n&&a[st]<0) st++;
while (ed>=1&&a[ed]<0) ed--;
int ans=0;
for (int i=st;i<=ed;i++){
if (a[i]>=0) {
if (i!=1&&a[i-1]>=0&&a[i]!=a[i-1]) ans++;
continue;
}
int j=i;
while (j<=ed&&a[j]<0) j++;
j--;
if (a[i-1]==a[j+1]&&a[i-1]==1) {
q1.push(j-i+1);
}
else if (a[i-1]==a[j+1]&&a[i-1]==0){
q2.push(j-i+1);
}
else ans++;
i=j;
}
while (!q1.empty()&&cnt1>=q1.top()){
cnt1-=q1.top();
q1.pop();
}
while (!q2.empty()&&cnt2>=q2.top()){
cnt2-=q2.top();
q2.pop();
}
while (!q1.empty()) {
q1.pop();ans+=2;
}
while (!q2.empty()){
q2.pop();ans+=2;
}
if (a[st]==1&&st!=1) q1.push(st-1);
else q2.push(st-1);
if (a[ed]==1&&ed!=n) q1.push(n-ed);
else q2.push(n-ed);
while (!q1.empty()&&cnt1>=q1.top()){
cnt1-=q1.top();
q1.pop();
}
while (!q2.empty()&&cnt2>=q2.top()){
cnt2-=q2.top();
q2.pop();
}
while (!q1.empty()) {
q1.pop();ans+=1;
}
while (!q2.empty()){
q2.pop();ans+=1;
}
cout<<ans<<endl;
}