给出一个
N
N
N的排列,每次可以花费
A
A
A的代价把任意区间向左循环转一格,或花费
B
B
B的代价向右循环转一格,求排序的最小代价。
N
≤
5000
N≤5000
N≤5000
题解
首先可以把操作简化一下,实质上就是花费
B
B
B的代价把某个数向左插入到任意位置,或花费
A
A
A的代价把某个数向右插入到任意位置。如果能想到这里,剩下的就更加自然了。
接着会发现,每个数最多被操作一次,且不操作的数必须构成单调上升子序列。
那么可以设
f
i
f_i
fi为执行到第
i
i
i个数且不操作的最小代价,枚举
j
j
j转移,需满足
a
j
<
a
i
a_j<a_i
aj<ai且
∀
x
∈
(
j
,
i
)
a
x
∉
[
a
j
,
a
i
]
forall xin(j,i) a_x
otin[a_j,a_i]
∀x∈(j,i)ax∈/[aj,ai],则再枚举中间每个数计算转移的代价。
为了方便,可以在排列最后加上一位正无穷。
这样是
O
(
N
3
)
O(N^3)
O(N3)的,但最后一维枚举可以省去,复杂度降为
O
(
N
2
)
O(N^2)
O(N2)。(其实
O
(
N
3
)
O(N^3)
O(N3)也可以过)
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 5010#define ll long long
ll f[N];int a[N];intmain(){int n, L, R, i, j;scanf("%d%d%d",&n,&R,&L);for(i =1; i <= n; i++)scanf("%d",&a[i]);
a[n +1]=2e9;
f[0]=0;for(i =1; i <= n +1; i++){
f[i]=1e16;
ll s =0;int mx =-1;for(j = i -1; j >=0; j--){if(a[j]< a[i]&& a[j]> mx) f[i]=min(f[i], f[j]+ s);if(a[j]> a[i]) s += R;else s += L, mx =max(a[j], mx);}}printf("%lld
", f[n +1]);return0;}