题意:
一个长度为 n 的数组,要求最多改动数组中的三个数,使得该数组变成一个等差数列。
题目保证有解。
思路:
我们先来思考一个问题,这个数列的公差是多少?
相信只要稍微思考一下,应该就可以得出先对这个数列的所有公差值记录一个出现次数,可以用map记录。然后求出出现次数最多的那个公差。
由于最多只能改动三个数,因此该数组的前1-4个数中至少有一个数是不需要改动的,因此我们只需要暴力枚举前4个数即可。
假设第一个数不需要改动,则用这个数和刚才求出的出现次数最多的那个公差,来构成一个新的数组。构造数组的同时来判定一共更改了几个数,如果更改的数小于等于3,则这是一个合法的结果,直接输出即可。
如果更改次数大于3,则此种情况不成立,继续枚举下一个点,最多枚举4次。
反思:
这个问题看到解法之后会发现非常简单,但是在比赛的时候却没有想到这么简单的做法,还是需要反思的。
在赛场上拿到一个题之后,我们需要思考这个题中究竟什么东西是可以确定的,而哪些东西是需要改变的,然后用确定的数去求出需要改变的数改变之后的值即可。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 1e5+100;
typedef long long LL;
int n;
LL a[N];
LL tmp[N];
LL d;
int ans;
map<LL,int> mp;
int solve(int x)
{
LL base = a[x];
int cnt = 0;
rep(i,1,n)
{
LL hp = base+d*(i-x);
if(hp == a[i]){
tmp[i] = hp;
}
else{
tmp[i] = hp;
cnt++;
}
if(cnt > 3) return 0;
}
return 1;
}
int main()
{
while(~scanf("%d",&n))
{
ans = 0;
mp.clear();
rep(i,1,n) scanf("%lld",&a[i]);
rep(i,2,n)
{
LL arc = a[i]-a[i-1];
mp[arc]++;
if(mp[arc] > ans){
ans = mp[arc];
d = arc;
}
}
rep(i,1,4)
{
if(solve(i))
{
rep(i,1,n-1)
printf("%lld ",tmp[i]);
printf("%lld\n",tmp[n]);
break;
}
}
}
return 0;
}