题目描述
这里,是梦开始的地方。
「天哪,这里怎么也会有这种丧题……」
这真的是最后一题了,Tweetuzki还没解出来的最后一题了。点开提交状态,红色的-1字样在漫漫的绿海中,如同脸上的一道疤一样显眼。
「完了,我AK的梦想要破灭了吗?」
这种离胜利只差一步,但却无法触及的感觉,最是令人焦虑。他徘徊彷徨,恳请思路的到来。
「!」
忽然一阵灵光从Tweetuzki脑中闪过。
「这不就是,最基础的那种算法!」
雀跃的指尖在键盘上跳动,一行行代码在屏幕上飞速显现出来。
Accepted.
「终于,达成了……」
是的,本次提高模拟赛的T1,就是这道题。Tweetuzki希望这里,也能够成为你梦开始的地方。
有一个长度为n的序列a1,a2,⋯,an。一次操作可以将某个元素的值加一或者减一,每个元素最多只能进行一次操作。求最少需要多少次操作,才能将这个序列变成一个等差数列。
注:我们称一个长度为m的数列b是一个等差数列,当且仅当对于任意的i(1≤i<m),都有bi+1−bi=b2−b1。特殊地,当m=1时,该数列也应当是等差数列。
「学长学长,我终于过这道D了诶!」
「Tweetuzki你怎么做得这么慢啊,半天才过这种傻逼题。」
输入
第一行输入一个正整数n(1≤n≤105),表示数列长度。
接下来一行输入n个正整数a1,a2,⋯,an(1≤ai≤109),描述这个数列。
输出
输出文件应包含一行一个整数。若无法操作出这样的等差数列,输出−1 即可;否则输出最小操作数。
样例输入
复制样例数据 4
24 21 14 10
样例输出
3
提示
可以将第1个元素增加1,第2个元素减少1,第3个元素增加1,形成[25,20,15,10]的等差数列。可以证明这是改变元素个数最少的情况,因此答案为3。
Subtask #1:n≤10。
Subtask #2:n≤20。
Subtask #3:n≤1 000。
Subtask #4:无特殊性质。
思路:其实题目有提示了(最基础的算法),一开始想复杂了其实暴力枚举即可;
1)首项a1有三种情况:a[1]+1 || a[1] || a[1]-1 (a[1]是输入的首项)
令 D=(a[n]-a[1])/(n-1)
2)公差d也有三种情况:D-1 || D || D+1 (可以证明最多三种,若数列{An}是一个公差为d等差数列改变后的数列,
( (An+1)-(A1-1) )/(n-1)=(d(n-1)-2)/(n-1)=d-2/(n-1),n>=2,时d最多改变2;D-1与D+2相差2;
一共有九种情况
然后根据首项和公差求出每一项是多少和原数列比较一下,看看改变了多少即可
代码:
const int maxn=1e5+5;
int a[maxn];
int n;
int slove(int cha,int a1)
{
int cnt=0;
int sum=0;
for(int i=1;i<=n;i++)
{
cnt=abs(a[i]-a1);///比较每一位的大小
if(cnt>1) return -1;///大于最大改变数,不行
sum+=cnt;
a1+=cha;
}
return sum;
}
int main()
{
cin>>n;
if(n==1) {cout<<0<<endl; return 0;}///注意判n==1的情况
int ans=n+5;
for(int i=1;i<=n;i++) cin>>a[i];
int cha=(a[n]-a[1])/(n-1);
///以下枚举9种情况
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{
int sum=slove(cha+i,a[1]+j);
if(sum!=-1) ans=min(ans,sum);
}
}
if(ans==n+5) cout<<-1<<endl;
else cout<<ans<<endl;
}