AGC032D Rotation Sort

AGC032D

有个排列 p i p_i pi,每次可以以 A A A的代价将某个数移到右边的一个位置,或者可以以 B B B的代价移到左边的一个位置。

问将其变成升序排列花的最小代价。

n ≤ 5 ∗ 1 0 3 n\le 5*10^3 n5103


考虑 p x = 1 p_x=1 px=1的位置:最终它一定要移到最左边。移到最左边有两种方法:花 B B B的代价将 p x p_x px移到最左边;花 A ∗ 左 边 的 数 的 个 数 A*左边的数的个数 A的代价将左边的数移到 p x p_x px的右边。注意到如果我们用了后面的这个操作,我们完全可以让 p x p_x px左边的数任意插入到 x x x右边的任意位置,于是可以先将 x x x右边的数排列好,直接按照顺序插进去一定是最优的。

第一种操作相当于把序列中的 1 1 1剔除掉,接着做剩下的子问题;第二种操作形成的子问题为原来的一段后缀。

于是可以DP: f i , j f_{i,j} fi,j表示考虑区间 [ i , n ] [i,n] [i,n],剔除掉了小于等于 j j j的数,这个问题的最小代价。

转移的时候需要一个辅助数组 c i , j c_{i,j} ci,j表示 [ j , i ] [j,i] [j,i]中大于 p j p_j pj的数的个数。

总时间复杂度 O ( n 2 ) O(n^2) O(n2)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#define N 5010
#define ll long long
int n;
ll A,B;
int p[N],re[N];
ll f[N][N];
int c[N][N];
void upd(ll &a,ll b){
    
    a=min(a,b);}
int main(){
    
    
//	freopen("in.txt","r",stdin);
	scanf("%d%lld%lld",&n,&A,&B);
	for (int i=1;i<=n;++i)
		scanf("%d",&p[i]),re[p[i]]=i;
	for (int j=1;j<=n;++j)
		for (int i=re[j]-1;i>=1;--i)
			c[j][i]=c[j][i+1]+(p[i]>j);
	memset(f,127,sizeof f);
	for (int j=0;j<=n;++j)
		f[n+1][j]=0;
	for (int i=n;i>=1;--i){
    
    
		f[i][n]=0;
		for (int j=n-1;j>=0;--j){
    
    
			if (c[j+1][i]==0)
				f[i][j]=f[i][j+1];
			else
				f[i][j]=min(f[i][j+1]+B,f[re[j+1]+1][j+1]+A*c[j+1][i]);
		}
	}
	printf("%lld\n",f[1][0]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/A1847225889/article/details/108858826
今日推荐