http://codeforces.com/problemset/problem/416/D
题意:给出n个数,其中包含正整数和-1,-1可代表任意正整数(必须大于0)。问n个数可以最少划分为多少个等差数列。
思路:贪心。让当前的数尽量属于前一个等差数列。用fir和sec维护两个连续的确定的数,并用arb维护fir前面可能属于该等差数列的第一个-1的位置。当fir和sec确定一个公差d后,要保证fir前面-1的位置是正整数,否则fir和sec之间的数分别属于两个等差序列。fir与sec依次后移,重复上述操作。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define LL long long
#define _LL __int64
using namespace std;
const int maxn = 200010;
int a[maxn],n,ans;
int main()
{
while(~scanf("%d",&n))
{
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
ans = 0;
bool fir = false,sec = false; //表示一个等差序列的前两个确定的数的布尔变量
int arb = -1;//标记该等差数列下fir前面的第一个-1的位置,初始化为-1说明当前等差数列没有前导的-1
int d,pre = -1;//pre保存前一个确定的数
for(int i = 1; i <= n; i++)
{
if(a[i] > 0)
{
if(!fir)
fir = true; //标记第一个数出现
else if(!sec)
{
if( (a[i] - a[pre]) % (i-pre) ) //当前数a[i]与前一个确定的数不能构成一个等差数列,那么划分为两个
{
arb = -1; //划分为两个后,第二个等差数列前面不会有-1,所以arb置为-1。
ans++;
}
else
{
d = (a[i]-a[pre]) /(i-pre);
if(arb == -1 || a[i]-1LL*(i-arb)*d > 0) //若当前等差数列前面有-1就计算-1的位置是否可能 <= 0
sec = true;
else
{//若 <= 0,也划分成两个数列
ans++;
arb = -1;
}
}
}
else
{
if(a[i] - a[pre] != 1LL*(i-pre)*d )//判断a[i]是否能加到前面的数列中
{
ans++;
sec = false;
arb = -1;
}
}
pre = i;
}
else
{
if(sec && a[pre] + 1LL*(i-pre)*d <= 0) //若-1不能加到前面的数列中,就开一个新的数列
{
fir = false;
sec = false;
ans++;
arb = i;
}
else if(arb == -1)
arb = i;
}
}
printf("%d\n",++ans);
}
return 0;
}