agc032D Rotation Sort
- 给你一个长度为
n
n
n的排列
a
n
a_n
an,通过以下两个操作将它排序:
- 花费
A
A
A将
a
i
a_i
ai向后移到任意一个位置。
- 花费
B
B
B将
a
i
a_i
ai向前移到任意一个位置。
- 求最小花费。
-
n
≤
5000
,
0
≤
A
,
B
≤
1
e
9
n\le5000,0\le A,B\le1e9
n≤5000,0≤A,B≤1e9
Solution
- 首先一个数只会移动一次,如果移动两次不如一次到位。
- 我们考虑哪些位置没有移动,这些位置一定是单调上升的,那么区间内的数一定要大于
a
r
a_r
ar或小于
a
l
a_l
al,并且一定要往两边移动,直接
n
2
n^2
n2计算这个dp即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 5005
#define ll long long
using namespace std;
int n,i,j,k,a[maxn],A,B,s[maxn][maxn];
ll f[maxn];
int pd(int l,int r){
return (s[r-1][a[r]-1]-s[l][a[r]-1])-(s[r-1][a[l]]-s[l][a[l]])==0;
}
ll cnt1(int l,int r){
return s[r-1][a[l]-1]-s[l][a[l]-1];
}
ll cnt2(int l,int r){
if (r==n+1) return 0;
return (s[r-1][n]-s[r-1][a[r]])-(s[l][n]-s[l][a[r]]);
}
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d%d%d",&n,&A,&B),a[0]=0,a[n+1]=n+1;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=1;i<=n;i++) for(j=1;j<=n;j++) s[i][j]=s[i-1][j]+(a[i]<=j);
memset(f,127,sizeof(f)),f[0]=0;
for(i=1;i<=n+1;i++) for(j=0;j<i;j++)
if (a[i]>a[j]&&pd(j,i))
f[i]=min(f[i],f[j]+cnt1(j,i)*B+cnt2(j,i)*A);
printf("%lld",f[n+1]);
}