标签:并且 最优 公式 不能 clu names 暴力 pac cin
给定一个序列,\(-1\)可以被替换成任意正整数,求该序列最少可以被分成多少个等差数列.(注意:分成的是子串而不是子序列)
一道比较难的贪心题。我们首先要整理:
等差数列的性质:指从第二项起,每一项与它的前一项的差等于同一个常数的一种数列
求公差的公式:开头与结尾的数的差\(/\)有几个数
根据题意,可以初步整理:
既然全部的数都是正整数,那么等差数列的公差一定要是整数,即不能为小数
只有一个已知数或者没有已知数答案是\(1\)
既然有\(-1\)干扰,我们一定要找两个确定的数去求等差数列,并且让\(-1\)一定要尽量最优
假设这\(2\)个数为\(left\)和\(right\),那么有right之前的数组成等差数列(不包括right),与right为left与一个新的right(在旧的right的右边)组成一个新的等差数列.
从上一个等差数列的结尾加一处开始找两个非-1的数,算出公差。如果公差为小数或者该公差导致前面的一串-1中有可能会有数变成非正数,就将第一个非-1的数和前面的一堆-1和两数间的一堆-1作为第一个数列,再以第二个非-1的数开头找等差数列。否则就再向后找可以合并到这个等差数列的数加入这个数列,找不到了就从这个等差数列的结尾加一处再找等差数列。
那么以上内容如何处理我会在代码注释说明
#include <bits/stdc++.h>
using namespace std;
int a[200000+15];
int main()
{
int n,now,left,right,k,ans=0;
//now是记录当前处理到哪里了,当然下面那个读入不算
//left和right如解释
//ans记录答案,k一会你们就知道了
cin>>n;
for(now=1; now<=n; ++now)
scanf("%d",&a[now]);
bool is=0;
long long cur,diff;//diff是公差,cur一会你们就知道了QAQ
now=1;
while(now<=n)//如果没处理完就继续
{
++ans;//没次ans+1
//下面的代码是找到2个非-1的数,很好理解
for(left= now; left<=n; ++left)
if(a[left]!=-1)
break;
for(right=left+1; right<=n; ++right)
if(a[right]!=-1)
break;
if(right>n)//right>n代表着right=n+1,即为只有1个整数或者没有整数,就直接推出ok
break;
diff= (a[right]-a[left])/(right-left);//公差公式
if(a[right]!=a[left]+ (right-left)*diff)
{
now= right;
continue;
}
/*
以上是判断公差是否为小数的
- 因为diff是long long,小数会自动转换(向下取整),所以就一定会有误差
- 然后如果他左边的是-1,因为-1最优,所以将他左边的数作为1个等差数列,下一次剩下的这个整数right再次处理即可
- 如果是整数,那么这个整数左边肯定有-1或没有-1,那么就同上了
*/
cur = a[left]-diff*(left-now);
//cur代表求出公差会让最前面的正整数变成什么数
if(cur<=0)
{
now=right;
continue;
}//如果变成非正数了,同上处理
//否则这个等差数列算上right,而且这个等差序列也可能继续往下
cur = a[right++]+diff;
//cur又摇身一变变成基准数啦(不要误会这个词语,是计算下一个数如果能算进等差数列的话应该是几)
while(right<=n)//暴力往下走
{
if(cur<=0)//如果变成非正数照样处理
break;
if(a[right]!=-1)//因为-1是最优值,所以不需要处理
{
if(a[right]!=cur)//当前的数不等于cur,代表错了哦
break;
}
++right;
cur+=diff;//别忘了更新基准数
}
now= right;//更新
}
cout<<ans<<endl;//输出答案
return 0;
}
[CodeForces 416D]Population Size
标签:并且 最优 公式 不能 clu names 暴力 pac cin
原文地址:https://www.cnblogs.com/lyfoi/p/9190327.html