【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;
}