P12675
题解
诡异的贪心。
有四种策略:
- 将头尾并成一样的,直接全赋值为
-10^{9} ; - 处理开头一段和结尾一段,使得这两段直接并在一起。
- 处理开头一段和结尾一段,将端点后的元素加成相同的,赋值中间一段。
- 处理开头一段和结尾一段,此时开头结尾这两段全为
-10^{9} ,取这两段的最靠近端点赋值中间一段。
对于一个点
先预处理
处理前缀
:::info[补:贪心证明]
可以发现答案至少为
处理完前后两端后,此时用上文第二个策略可以使得多余贡献至多为
考虑让端点前后的点相等,可以只加一次,可能会更优。对于端点靠在一起的情况不需要多余操作,也会更优。 :::
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
const ll inf=1e15;
int _;
int n;
ll a[N],c1[N],c2[N],ans;
ll mnc,pl1,pl2;
void solve() {
memset(c1,0,sizeof c1),memset(c2,0,sizeof c2);
scanf("%d",&n),mnc=inf,pl1=pl2=0;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
ans=abs(a[n]-a[1]);
for(int i=2;i<n;i++) {
c1[i]=abs(a[i]-a[1]),c2[i]=abs(a[n]-a[i]);
if(i>2) ans=min(ans,c2[i]+c1[i-1]);
if(pl1!=i-1&&pl1!=i-2) ans=min(ans,c2[i]+mnc+min(2ll,abs(a[pl1+1]-a[i-1])));
if(pl2!=i-1&&pl2!=i-2) ans=min(ans,c2[i]+mnc+min(2ll,abs(a[pl2+1]-a[i-1])));
if(c1[i]<mnc) pl1=i,pl2=0,mnc=c1[i];
if(c1[i]==mnc) pl2=i;
}
printf("%lld\n",ans+n);
return;
}
int main() {
scanf("%d",&_);
while(_--) solve();
return 0;
}