bzoj1341 [Baltic2007]Ranklist sorting名次排序问题(dp)

好神啊orz
论文题orz
论文传送门:portal
题意转化一下大概就是:给你一个1~n的排列,要求你用最小的代价把它变成1,2,..n。
只能用一种操作:i,j表示把位置i上的数挪到第j个位置,其余数的相对位置不变,代价为i+j。
首先我们有一些贪心的性质:考虑数n所在的位置,我们如果移动它,一定是直接移动到位置n,且一定第一个移动它。
推广一下,就是我们从大到小考虑每一个数x,只有两种决策:
1.如果x+1在x的后面,那么x可以不动,以后再将所有位于x和x+1之间的数移动到x前面。
2.直接把x移动到x+1的前一个位置上。
因为位置在不断改变,很难设计状态,根据我们的决策顺序,我们可以发现,当我们决策数x的时候,所有<=x的数的相对位置是没有变化的,而>=x+1的数此时都一定在x+1及其后面,因此x现在的位置就是pos[x]之前< x的数的个数+1!而x+1因为可能移动了,我们不知道他在原序列中的位置,因此我们要枚举x+1的位置!

考虑dp[i][j],表示把数i移动到j位置,且i+1~n均已在j位置及其后的最小花费。
那么我们有dp[i][j]=dp[i+1][j]+vi+vj-1。其中vi和vj表示i,j现在的实际位置。因为是把数i挪到数j的前一个位置,所以要-1。

如果我的x不动,那么可能会给我以后的决策花费带来影响,设x的位置为p1,x+1的位置为p2,则对于p1~p2之间的小于x的数y都不得不跨过y+1…x的数因此会给未来的决策带来一个固定的额外花费x-y.
因为当前决策对未来“行动”的费用影响只与当前决策有关,所以我们可以把对未来的费用影响直接加到当前决策中去。

也就是说,我们有转移方程为:
dp[i][j]=dp[i+1][j]+vi+vj
dp[i][pos[i]]=min{dp[i+1][j]+k=pos[i]+1j1ix[k]|x[k]<i |pos[i]<j}

复杂度 O(n2)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,pos[N],dp[N][N],pre[N],ans;//dp[i][j],把数i移动到j位置,且i+1~n均已在j位置及其后的最小花费
struct node{
    int id,x;
    friend bool operator<(node a,node b){return a.x>b.x;}
}a[N];
inline bool cmp(node a,node b){return a.id<b.id;}
void dfs(int i,int j){
    if(i==n) return;
    if(pos[i]!=j) ++ans,dfs(i+1,j);
    else dfs(i+1,pre[i]);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();for(int i=1;i<=n;++i) a[i].x=read(),a[i].id=i;
    sort(a+1,a+n+1);for(int i=1;i<=n;++i) pos[i]=a[i].id,a[i].x=i;
    sort(a+1,a+n+1,cmp);++n;memset(dp,inf,sizeof(dp));dp[n][n]=0;a[n].x=n;
    for(int i=n-1;i>=1;--i){
        int v1=1,v2=1;
        for(int j=1;j<pos[i];++j) v1+=(a[j].x<i);
        for(int j=1;j<=n;++j){
            v2+=(a[j].x<i);
            if(dp[i+1][j]!=inf) dp[i][j]=dp[i+1][j]+v1+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(a[j].x<i) tmp+=i-a[j].x;
        }
    }ans=inf;int x=0;
    for(int i=1;i<=n;++i) if(dp[1][i]<ans) x=i,ans=dp[1][i];
    ans=0;dfs(1,x);printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Icefox_zhx/article/details/80014541