非零段划分 代码长度595B C语言 正确 100分 耗时46ms 空间使用6.246MB 2021-11-06完成
应该会慢慢更新,0算法基础极限挑战今年12月得CSP。坚持每天一道
题目描述
A1,A2,…,An是一个由 n 个自然数(非负整数)组成的数组。我们称其中 Ai,…,Aj 是一个非零段,当且仅当以下条件同时满足:
·1≤i≤j≤n;
·对于任意的整数 k,若 i≤k≤j,则 Ak>0;
·i=1 或 Ai-1=0;
·j=n 或 Aj+1=0。
下面展示了几个简单的例子:
·A = [3, 1, 2, 0, 0, 2, 0, 4, 5, 0, 2] 中的 4 个非零段依次为 [3, 1, 2]、[2]、[4, 5] 和 [2];
·A = [2, 3, 1, 4, 5] 仅有 1 个非零段;
·A = [0, 0, 0] 则不含非零段(即非零段个数为 0)。
现在我们可以对数组 A 进行如下操作:任选一个正整数 p,然后将 A 中所有小于 p 的数都变为 0。试选取一个合适的 p,使得数组 A 中的非零段个数达到最大。若输入的 A 所含非零段数已达最大值,可取 p = 1,即不对 A 做任何修改。
输入格式
从标准输入读入数据。
输入的第一行包含一个正整数 n。
输入的第二行包含 n 个用空格分隔的自然数 A1, A2, … , An。
输出格式
输出到标准输出。
仅输出一个整数,表示对数组 A 进行操作后,其非零段个数能达到的最大值。
样例1输入
11
3 1 2 0 0 2 0 4 5 0 2
样例1输出
5
样例1解释
p = 2 时,A = [3, 0, 2, 0, 0, 2, 0, 4, 5, 0, 2],5 个非零段依次为 [3]、[2]、[2]、[4, 5] 和 [2];此时非零段个数达到最大。
样例2输入
14
5 1 20 10 10 10 10 15 10 20 1 5 10 15
样例2输出
4
样例2解释
p = 12 时,A = [0, 0, 20, 0, 0, 0, 0, 15, 0, 20, 0, 0, 0, 15],4 个非零段依次为 [20]、[15]、[20] 和 [15];此时非零段个数达到最大。
样例3输入
3
1 0 0
样例3输出
1
样例3解释
p = 1 时,A = [1, 0, 0],此时仅有 1 个非零段 [1],非零段个数达到最大。
样例4输入
3
0 0 0
样例4输出
0
样例4解释
无论 p 取何值,A 都不含有非零段,故非零段个数至多为 0。
子任务
1、70% 的测试数据满足 n ≤ 1000;
2、全部的测试数据满足 n≤5×10^5,且数组 A 中的每一个数均不超过 10^4
子任务说明什么:满足第一条得70分,满足第二条得100分,一般要注意第二条容易超时,所以要简化算法。
思路:
借用岛屿情况来分析这个题。考虑i足够大的情况,所有的数都被海水淹没了,只有0个岛屿。然后,海平面逐渐下降,岛屿数量出现变化。每当一个凸峰出现,岛屿数就会多一个;每当一个凹谷出现,原本相邻的两个岛屿就被这个凹谷连在一起了,岛屿数减少一个。x[i]为所淹到的高度。
ps:我看其他博主写的是凸峰+1,凹峰-1,if语句中与我相反。
for(i=1;i<j;i++)
{
if(x[i-1]<x[i]&&x[i]>x[i+1])
{
s[x[i]]--; //若凸峰被淹没 则岛屿-1
}
if(x[i-1]>x[i]&&x[i]<x[i+1])
{
s[x[i]]++; //若凹峰被淹没 则岛屿+1
}
}
若相邻的数相等,则可以看作一个凸峰,所以在上述操作前简化数组,对于相邻相等的数,只保留一个。x[0]和x[j]为0设为边界,则不用考虑首末项的特殊情况,简化代码
x[0]=0; //x[0]和x[j]为0设为边界,简化代码
x[1]=num[0];
for(i=1;i<n;i++)
{
if(num[i]!=num[i-1])
{
x[j]=num[i];
j++;
};
};
x[j]=0; //共j+1个数
考虑初始情况:在i=0时,至少有1个凸峰,若所有数全部相等也有1个凸峰。极限情况:所有数全为0则没有凸峰。判断是否为极限情况,初始化凸峰数值。
for(i=0;i<j+1;i++)
{
if(x[i]!=0)
{
sum=1;
break;
}
}
最大值则只需要从s[i]得前缀和中找出最大值就是所要的结果。
以下为全部代码:
#include
int main()
{
int n,i,m=0,j=2,sum=0,fin=0;
int s[10000]={0};
scanf("%d", &n);
int num[n];
int x[n];
for(i=0;i<n;i++)
{
scanf("%d", num+i);
};
x[0]=0; //x[0]和x[j]为0设为边界,简化代码
x[1]=num[0];
for(i=1;i<n;i++)
{
if(num[i]!=num[i-1])
{
x[j]=num[i];
j++;
};
};
x[j]=0; //共j+1个数
for(i=0;i<j+1;i++)
{
if(x[i]!=0)
{
sum=1;
break;
}
}
for(i=1;i<j;i++)
{
if(x[i-1]<x[i]&&x[i]>x[i+1])
{
s[x[i]]--; //若凸峰被淹没 则岛屿-1
}
if(x[i-1]>x[i]&&x[i]<x[i+1])
{
s[x[i]]++; //若凹峰被淹没 则岛屿+1
}
}
for(i=0;i<10000;i++)
{
sum+=s[i];
fin=sum>fin?sum:fin;
}
printf("%d",fin);
return 0;
}