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

http://www.elijahqi.win/archives/3147
Description

已知参赛选手的得分,你的任务是按照得分从高到底给出选手的排名。遗憾的是,保存选手信息的数据结构只支持
一种操作,即将一个选手从位置i移动到位置j,该移动不改变其他选手的相对位置,即如果i>j,位置j和位置i-1
之间的选手的位置都比原来加1,相反如果i< j,则位置i+1和位置j之间的选手的位置都比原来减一。上述移动的操
作的代价定义为i+j,这里,位置编号从1开始。请你编程确定一个移动选手的步骤,将选手按照得分从高到低排序
,并使整个移动过程的代价最小。
Input

第一行为一个整数n(2<=n<=1000),表示选手的人数;
接下来的n行,每行一个非负整数Si( 0<=Si<=1000000),表示一个选手的得分。你可以认为每人的得分是不同的。
Output

第一行为一个整数,表示移动的次数

Sample Input

5
20
30
5
15
10
Sample Output

2
HINT

Source

论文题:https://wenku.baidu.com/view/83d0a76925c52cc58bd6bea8.html

坑死 调不出

考虑原题的性质1:每个数可以主动动 被动动 但是一定是每个数主动动一次 并且动的话一定是主动把x这个挪动到x+1的前一个位置上 要么就还在原位置等待其他数把自己推过去 另一个性质 按照编号从大到小操作 好了这样就很明显的划分了状态 如果是上面不移动 那么就是把我x,x+1之间的数字都移出去移到 x前面即可

那么现在有了一个指数级的搜索的做法 因为这个题的状态没法记录 状态数太大了 不好记录 如何 考虑每个数的相对位置的关系 如果我现在需要操作x并且x+1都是操作好的并且x-1都是没动的 那么显然他们的相对厝都是排好顺序的那么我们不妨不要每次去移动这个状态而是从相对位置下手 并且可以发现 在本题中 相对位置可以算出原有的位置 从而计算出贡献 那么很好此题可以用动态规划解决了

设c[i][j]当前在i号位置 完成了j+1~n 比j小的数有几个 s[i][j]当前在i号位置 完成了j+1~n 比j小的数有几个

那么c[i][j]+1就是我们每次想要计算的位置 那么这样的话 如果每次算的x都在x+1的前面这样是对的了 但是情况显然没有这么单纯他非常的复杂有可能x+1不移动 那么导致x+1一开始在x的前面 后来计算位置信息的时候是不会考虑到这些问题的 怎么办这时候就需要s数组了 每次我在做x的时候如果我钦定x不动那么x~x+1之间的数说明以后我一定会动他们 并且以后这部分贡献不好计算不妨就在当前提前把这部分费用计算出来 那么以后再计算答案的时候就并不需要考虑这部分贡献了

最后统计答案 因为我的移动都会将他们移动到一个位置 当然如果一个没有移动 那么就记录一下pre

表示上一个位置 最后去dfs一下即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=1e3+10;
const int inf=0x3f3f3f3f;
int a[N],c[N][N],n,s[N][N],cnt[N],dp[N][N],pre[N],pos[N],ans;
inline void dfs(int x,int now){
    if (x==n) return;
    if(pos[x]!=now) ++ans,dfs(x+1,now);
    else dfs(x+1,pre[x]);
} 
struct node{
    int id,x;
}aa[N];
inline bool cmp1(const node &a,const node &b){return a.x>b.x;}
int main(){
    freopen("bzoj1341.in","r",stdin);
    n=read();for (int i=1;i<=n;++i) aa[i].x=read(),aa[i].id=i;
    sort(aa+1,aa+n+1,cmp1);
    for (int i=1;i<=n;++i) pos[i]=aa[i].id,aa[i].x=i,a[aa[i].id]=i;
    ++n;a[n]=n; 
    for (int i=1;i<=n;++i){  //c[i][j]当前在i号位置 完成了j+1~n 比j小的数有几个
        //s[i][j]当前在i号位置 完成了j+1~n 比j小的数有几个
        for (int j=1;j<=n;++j) s[i][j]=s[i-1][j];
        for (int j=a[i]+1;j<=n;++j) s[i][j]+=a[i],++cnt[j];
        for (int j=1;j<=n;++j) c[i][j]=cnt[j];
    }memset(dp,0x3f,sizeof(dp));dp[n][n]=0;
    for (int i=n-1;i;--i){
        for (int j=1;j<=n;++j)
            if(dp[i+1][j]!=inf) dp[i][j]=dp[i+1][j]+c[pos[i]-1][i]+1+c[j][i]+1;
        for (int j=pos[i]+1;j<=n;++j){static int tmp;
            tmp=(c[j-1][i]-c[pos[i]][i])*i-(s[j-1][i]-s[pos[i]][i]);
            if(dp[i+1][j]+tmp<dp[i][pos[i]]) dp[i][pos[i]]=dp[i+1][j]+tmp,pre[i]=j;
        }
    }ans=0x3f3f3f3f;int id=0;
    for (int i=1;i<=n;++i) if (dp[1][i]<ans) ans=dp[1][i],id=i;
    ans=0;dfs(1,id);printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值