问题 M: 梦开始的地方

题目描述

这里,是梦开始的地方。
「天哪,这里怎么也会有这种丧题……」
这真的是最后一题了,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;
}

发布了95 篇原创文章 · 获赞 7 · 访问量 8470

猜你喜欢

转载自blog.csdn.net/Spidy_harker/article/details/98110078