题解:贪心
题目大意是用两种车轮覆盖一段长度为n的区间,第一种车轮数量任意但是只能覆盖数字大于等于0的地方,另一种车轮数量为k,可以覆盖任意的区域。求更换车轮的最少次数。
这道题有一个很明显的贪心,那就是尽可能将两个负数区间之前的区域也用数量受限的车轮覆盖,那么我们可以将这些区间的长度排序,然后能覆盖就覆盖。
但是直接这么做是不对的。因为中间的区域被覆盖会使次数减少2,但是最头上的区间如果被覆盖成与第一个负数相同的,因为最开始是数量任意的轮子,所以这依然算一次更换,所以对于最头上的区间来说没有影响,直接不考虑即可。而对于最后一个负数位置之后的区域对答案的影响只能减少1,所以我单独考虑最后一个区间,进行分类讨论。中间的按照贪心直接做即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 200003
using namespace std;
int n,m,a[N],cnt,mark[N],mark1[N];
struct data
{
int l,r,len;
}c[N];
int cmp(data a,data b)
{
return a.len<b.len;
}
int main()
{
//freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
int pos=1;
while (a[pos]>=0&&pos<=n) pos++;
m--; mark[pos]=1;
if (pos>n) {
printf("0\n");
return 0;
}
for (int i=1;i<=pos-1;i++) mark[i]=1;
for (int i=pos+1;i<=n;i++)
if (a[i]<0) {
mark[i]=1;
c[++cnt].l=pos+1; c[cnt].r=i-1;
c[cnt].len=c[cnt].r-c[cnt].l+1;
m--; pos=i;
}
if (m<0) {
printf("-1\n");
return 0;
}
//c[++cnt].l=pos+1; c[cnt].r=n;
//c[cnt].len=c[cnt].r-c[cnt].l+1;
if (cnt) sort(c+1,c+cnt+1,cmp);
int t=n-pos; //cout<<pos<<" "<<n<<endl;
int m1=m-t; int ans=0,ans1=0;
if (m1>=0) {
for (int i=1;i<=n;i++) mark1[i]=mark[i];
for (int i=pos+1;i<=n;i++) mark1[i]=1;
for (int i=1;i<=cnt;i++) {
if (c[i].len<=0) continue;
if (c[i].len>m1) break;
for (int j=c[i].l;j<=c[i].r;j++) mark1[j]=1,m1--;
}
if (mark1[1]==1) ans1++;
for (int i=2;i<=n;i++)
if (mark1[i]!=mark1[i-1]) ans1++;
}
else ans1=1000000000;
for (int i=1;i<=cnt;i++) {
if (c[i].len<=0) continue;
if (c[i].len>m) break;
for (int j=c[i].l;j<=c[i].r;j++) mark[j]=1,m--;
}
if (mark[1]==1) ans++;
for (int i=2;i<=n;i++)
if (mark[i]!=mark[i-1]) ans++;
printf("%d\n",min(ans1,ans));
}