【BOI2007】Ranklist Sorting

【BOI2007】Ranklist Sorting

Description

有一个长为\(n\)的排列\(p\)

每次可以选两个位置\(i,j\),然后将\(p_i\)放到\(j\)的位置上,代价为\(i+j\)

求将排列变为降序的最小代价(原题要求构造方案)

Input

第一行一个数\(n\)

然后读入排列

Output

一行一个数表示答案

Sample Input

4
2 3 1 4 

Sample Output

8

Data Constraint

\(1\le n\le 2000\)

Solution

OI倒退十年是吧

首先为了方便理解,可以把降序看成升序

每个数分为两种:动与不动

显然动点只会移动一次

然后可以通过调整证明:动点从大到小/从小到大移到对应位置一定不会更劣

考虑\(dp\)

\(f_{i,j}\)表示从大到小填到\(i\)\(i\)要放在原序列\(j\)的位置上

此时\(i+1-n\)已经按升序排好,\(1-i\)的相对顺序与原序列相同

那么对于每个数,我们可以计算出其当前位置,记为\(now_i\)

\(i\)动时,\(i\)一定要放在\(i+1\)的前面,即\(f_{i,j}=\min(f_{i,j},f_{i+1,j}+now_i+now_j)\)

\(i\)不动时,这一步决策会对后面的贡献值造成影响,但是我们可以将其提前计算

具体来说,设\(pos_i\)表示\(i\)在原序列中的位置

对于枚举的\(j\)\(k\in [pos_i+1,j-1]\)\(k\)一定会跨过\(i\),此时\([p_k+1,i]\)这些数全部出现

对其造成的影响就是\(\max(i-p_k,0)\)

于是有\(f_{i,pos_i}=\min(f_{i,pos_i},f_{i+1,j}+\sum_{k=pos_i+1}^{j-1}\max(i-p_k,0))\)

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 2010

int f[N][N],p[N],n,pos[N],ans;

int main(){
	scanf("%d",&n);
	F(i,1,n)scanf("%d",&p[i]),p[i]=n-p[i]+1,pos[p[i]]=i;
	p[n+1]=pos[n+1]=n+1;
	memset(f,127,sizeof(f));
	ans=f[0][0];
	f[n+1][n+1]=0;
	Fd(i,n,1){
		int p1=1,p2=1;
		F(j,1,i-1)p1+=pos[j]<pos[i];
		F(j,1,n+1){
			p2+=p[j]<i;
			f[i][j]=min(f[i][j],f[i+1][j]+p1+p2);
		}
		int sum=0;
		F(j,pos[i]+1,n+1){
			f[i][pos[i]]=min(f[i][pos[i]],f[i+1][j]+sum);
			sum+=max(i-p[j],0);
		}
	}
	F(i,1,n+1)ans=min(ans,f[1][i]);
	printf("%d",ans);
	return 0;
}
posted @ 2022-11-16 21:15  冰雾  阅读(36)  评论(0编辑  收藏  举报