vijos1661
我能说这破题坑了我一上午???
就因为一个东西手滑写在了min里面。。。。
这道题如果正着考虑,情况太tmd多了,因为可以连续施法。。。
然而如果倒过来,我们认为连着j次施法,就是让一个i位置上的数字移动到i+j位置来,那么改变的信息其实不多。
所以我们可以枚举i和j,然后来设计一下状态
f[i][0]表示i这个点没有做过移动的最小代价
f[i][1]表示i这个点做过移动的最小代价
因为每个点不能重复施法,所以如果一个点做过移动,那么当前位置上一定是i+1
状态转移,f[i][0]=min(f[i+1][0]+abs(a[i]-a[i+1]),f[i+1][1]+abs(a[i]-a[i+2))
这个好处理。。。
接下来是f[i][1],我们假设它移动到j的位置,大家可以画图理解一下。
f[i][1]=min(f[i][1],min(f[j+1][0]+abs(a[i]-a[j+1]),f[j+1][1]+abs(a[i]-a[j+2]))+abs(a[i]-a[j])+sum[j]-sum[i+1]);
这里解释一下sum,sum[i]表示1~i不做任何处理的代价,那么这个显然满足区间减法,我们把i移到j,中间一大堆的代价显然不变,所以可以直接前缀和处理。
以及我们要特殊处理j=n的情况。。很显然(大雾
f[i][1]=min(f[i][1],sum[n]-sum[i+1]+abs(a[i]-a[n]));
破题害人不浅!
注意我们可能枚举到a[n+1],所以a[n+1]要赋为a[n]
CODE:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<string>
#include<map>
#include<cstring>
#include<vector>
#define inf 1e9
#define ll long long
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Dow(i,j,k) for(int i=k;i>=j;i--)
using namespace std;
int f[5001][2],a[5001],n,sum[5001];
int main()
{
scanf("%d",&n);
For(i,1,n)
scanf("%d",&a[i]),sum[i]=sum[i-1]+(i==1?0:1)*abs(a[i]-a[i-1]);
a[n+1]=a[n];
For(i,1,n*2) f[i][1]=f[i][0]=inf;
f[n][1]=f[n][0]=0;
Dow(i,1,n-1)
{
f[i][0]=min(f[i+1][1]+abs(a[i+2]-a[i]),f[i+1][0]+abs(a[i+1]-a[i]));
For(j,i+1,n-1)
f[i][1]=min(f[i][1],min(f[j+1][0]+abs(a[i]-a[j+1]),f[j+1][1]+abs(a[i]-a[j+2]))+abs(a[i]-a[j])+sum[j]-sum[i+1]);
f[i][1]=min(f[i][1],sum[n]-sum[i+1]+abs(a[i]-a[n]));
}
printf("%d\n",min(f[1][1],f[1][0]));
}