【Gym - 101848B】Almost AP【等差数列改三个数】

题意:

       一个长度为 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82260917