论文题(挺考验语文阅读能力的,足足花了我一晚上+一下午)
首先感性地得到两个推论(这是我们后续按顺序 dp 的基础):
- 最有决策中一定是从大往小操作
- 对于数 x 来说,要么在原地不动,要么移动到 x+1 的前一个位置
现在我们修改一下这个操作的定义:
我们不改变没有操作的点的位置,而是 并列放置 。这样的放置对后面 dp 转移方程的推导很有帮助。
我们可以令 f [ x ] [ p 2 ] f[x][p2] f[x][p2] 表示把 x x x 移动到原序列的 p 2 p2 p2 位置且 x + 1 x+1 x+1 到 n n n 都在 p 2 p2 p2 的最小费用。 p o s [ x ] pos[x] pos[x] 表示 x x x 在原序列中位置。
先来考虑 p o s [ x ] < p 2 pos[x]<p2 pos[x]<p2 即往后移动的情况。
我们定义 c ( p , x ) c(p,x) c(p,x) 表示 1 到 p 中小于 x 的位置的个数 + 1。那么 x 的真实位置其实就是 c ( p , x ) c(p,x) c(p,x) 。
有方程 f[x][p2]=f[x+1][p2]+c(p,x)+c(p2,x) (之所以 -1 是因为要移动到 x+1 的前一位)
那么 p o s [ x ] > p 2 pos[x]>p2 pos[x]>p2 呢?
显然可以归到上一种情况,即 f[x][p2]=f[x+1][p2]+c(p,x)+c(p2,x)+(4-3) (注意这里 (4-3) 其实是提前计算的,后面会讲)
下面我们考虑如何计算对未来状态的影响。
还是考虑 图2.7
。这个时候 5 都对未来决策造成了影响(3 的位置变成了 c(5,3)+2),(注意这里其实是 5 对 3 的位置造成了影响,和 4 没有任何关系),换句话说对象应该是 所有
p
<
x
<
p
2
p<x<p2
p<x<p2 且
s
[
x
]
<
i
s[x]<i
s[x]<i 的点,而且对每个 s[x] 的影响恰好为
i
−
s
[
x
]
i-s[x]
i−s[x] 。
综上所述,dp 转移方程为:
- f [ i ] [ j ] = f [ i + 1 ] [ j ] + c ( p o s [ i ] , i ) + c ( j , i ) f[i][j]=f[i+1][j]+c(pos[i],i)+c(j,i) f[i][j]=f[i+1][j]+c(pos[i],i)+c(j,i)
- f [ i ] [ p o s [ i ] ] = min ( f [ i + 1 ] [ j ] + ∑ p o s [ i ] < x < j [ s [ x ] < i ] ( i − s [ x ] ) ∣ j > p o s [ i ] ) f[i][pos[i]]=\min(f[i+1][j]+\sum_{pos[i]<x<j}[s[x]< i](i-s[x])|j>pos[i]) f[i][pos[i]]=min(f[i+1][j]+∑pos[i]<x<j[s[x]<i](i−s[x])∣j>pos[i])
时间复杂度 O ( n 2 ) O(n^2) O(n2) (优秀!)
最后我们写一个 dfs 输出方案即可。
Luogu 唯二 AC 解 (233)
#include<bits/stdc++.h>
#define db double
#define ll long long
#define mkp make_pair
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
const int Maxn=1005;
pii a[Maxn];
int n,tot,pos[Maxn],s[Maxn],dp[Maxn][Maxn],pre[Maxn],l[Maxn][Maxn],r[Maxn][Maxn],add[Maxn];
pii b[Maxn];
bool cmp(pii x,pii y) {
return x.fi>y.fi||x.fi==y.fi&&x.se<y.se;
}
void dfs(int i,int j) {
if(i==n) return;
if(pos[i]!=j) {
dfs(i+1,j);
//输出把当前点的实际位置移动到 j 的实际位置
tot++;
b[tot].fi++;
b[tot].fi+=add[i];
b[tot].se++;
//交换到的位置不变 ?
for(int k=1;k<=j;k++) {
if(s[k]<i) b[tot].se++;
}
for(int k=1;k<pos[i];k++) {
if(s[k]<i) b[tot].fi++;
}
}
else {
//对后面的决策造成影响
for(int k=pos[i]+1;k<pre[i];k++) {
if(s[k]<i) {
add[s[k]]+=i-s[k];
}
}
dfs(i+1,pre[i]);
}
}
int main() {
memset(dp,0x3f,sizeof dp);
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i].fi;
a[i].se=i;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++) {
pos[i]=a[i].se;
s[a[i].se]=i;
}
n++,dp[n][n]=0,s[n]=n;
// for(int i=1;i<=n;i++) {
// printf("%d\n",s[i]);
// }
for(int i=n;i>=1;i--) {
int v1=1,v2=1;
for(int j=1;j<pos[i];j++) {
if(s[j]<i) {
v1++;
}
}
for(int j=1;j<=n;j++) {
if(s[j]<i) {
v2++;
}
if(dp[i+1][j]+v1+v2<dp[i][j]) {
dp[i][j]=dp[i+1][j]+v1+v2;
l[i][j]=v1;
r[i][j]=v2;
}
}
int tmp=0;
for(int j=pos[i]+1;j<=n;j++) {
if(dp[i+1][j]+tmp<dp[i][pos[i]]) {
dp[i][pos[i]]=dp[i+1][j]+tmp;
pre[i]=j;
}
if(s[j]<i) {
tmp+=i-s[j];
}
}
}
int ans=inf,xo=0;
for(int i=1;i<=n;i++) {
if(dp[1][i]<ans) {
ans=dp[1][i];
xo=i;
}
}
dfs(1,xo);
printf("%d\n",tot);
for(int i=1;i<=tot;i++) {
printf("%d %d\n",b[i].fi,b[i].se);
}
}