QWQ啊

这个题可以说是我目前碰到过的最难理解的dp之一了。

题目大意:

已知参赛选手的得分,你的任务是按照得分从高到底给出选手的排名。遗憾的是,保存选手信息的数据结构只支持

一种操作,即将一个选手从位置i移动到位置j,该移动不改变其他选手的相对位置,即如果i>j,位置j和位置i-1

之间的选手的位置都比原来加1,相反如果i<j,则位置i+1和位置j之间的选手的位置都比原来减一。上述移动的操

作的代价定义为i+j,这里,位置编号从1开始。请你编程确定一个移动选手的步骤,将选手按照得分从高到低排序

,并使整个移动过程的代价最小。

其中

第一行为一个整数n(2<=n<=1000),表示选手的人数;

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

求交换次数

QAQ啊 一看这个题目,基本是没有任何思路;

唯一想到的就是问题转换了

把原序列按照从大到小的顺序排序,然后将他们的值,赋值为排序完数组的下标

那么这个题就转化为用最小的代价将序列转化成1~n的排列

然后.....gg

这里强势安利一位dalao的论文!

https://wenku.baidu.com/view/83d0a76925c52cc58bd6bea8.html###

徐源盛-算法合集之《对一类动态规划问题的研究》

首先,我们要可以先大胆猜想一下!

1>一个人最多移动一次,如果移动则必然移动到编号比他大1的数的前面(因为往后移动会使部分人的位置减 1,从而减少此人未来的移动费用,所以编号越大的越先移动。)

2>按标号从大到小移动(这个,,,,,显然呀感性理解一下固定好了编号大的 不就不用移动这些了呀~)

那么,

按编号从大到小进行操作,对于每个人 x 有两种选择:

1. 移动到 x+1 前一个位置。

2. 如果 x 在 x+1 前,则不移动,以后再将所有处于 x 和 x+1 之间的移动到 x 之前。

同时,我们又能得到两个推论

所有小于等于 x 的数的相对位置与最初的一样

所有比 x+1 大的数都位于 x+1 后面

这两条非常关键!!!!

我们考虑dp状态是

f[i][j]表示i这个数移动到原序列的j位置且i+1~n都在i后面的最小代价

对于一个i i+1的位置我们并不能确定,所以是需要枚举的!

首先首先

我们定义一个count函数 count(p,x)表示在原序列的第1~p个数中,有多少个比x小的数

那么由刚才那两个推论,就可以得知:

x 的实际位置为最开始 1 到 p1 中比 x 小的数的个数加 1(加上自己)

接下来,我们就可以分类讨论了

对于一个数x,一定是主动移动(移到x+1前面),或者被动移动(将x和x+1之间的数都移走)

主动移动的话:

pos[i]是表示i这个数在原序列的位置是多少

首先考虑当   j>pos[i]

f[i][j]=f[i+1][j]+count(pos[i],i)+count(j,i+1)-1 (-1是因为要移动到x+1的前一位的代价)

而当 j<pos[i]时

f[i][j]=f[i+1][j]+count(pos[i],i)+1 + count(j,i+1)-1  (这里+1是因为因为i+1在i前,所以i的实际位置就是count(pos[i],i)+1)

(反正我是这么理解的QWQ论文里也没给出一个详细的解释)

int aa=count(pos[i],i);
int bb=1;
for(int j=1;j<=pos[i]-1;j++)
{
if (a[j].x<i+1) bb++;
if (f[i+1][j]<f[1005][1005]) f[i][j]=f[i+1][j]+aa+bb; //之所以不用减1是因为此时i的位置是aa+1并非aa 然后+1和-1正好抵消了
}
bb++;
for (int j=pos[i]+1;j<=n;j++)
{
if (a[j].x<i+1) bb++;
if (f[i+1][j]<f[1005][1005]) f[i][j]=f[i+1][j]+aa+bb-1;
}

被动移动的话:

由于一个性质就是:

当前决策对未来

“行动”的费用影响只与当前决策有关

比如在两男两女中选一男一女去执行任务,已知每个人的效率,希望总效率最高。并且男 A 如果被选,所有女生的效率加 7,如果男 B 被选,所有女生的效率减 7。在第一阶段选男 A 还是男 B 对第二阶段女生的效率有不同的影响,可以将对女生的影响当做男生的“自身魅力”,即把男 A 的效率加 7,男 B 的效率减7。而我们实际上是把第二阶段的费用在第一阶段计算了。对于这类问题我们往往将对未来“行动”的影响一并算做当前决策的费用。

所以,当x这个数不动,那么所有位置在pos[i]+1到j-1之间的数,都需要跨越i

那么也就是

在j>pos[i]的基础上:

f[i][pos[i]]=min{f[i][pos[i]], f[i+1][j]+sigma(i-s[k]) (i>s[k]) pos[i]<k<j

因为对于一个数x(x必定小于i,可以根据推论得知),x需要跨过它而多增加的代价就是i-x(可以感性理解一下,就好比5 3 4如果5不动 那么4需要多增加1的代价,3增加2,因为3既要跨越5,又要跨越4)

如果这个数不动的话,需要记录一下i+1的位置是哪,用一个pre去记录,便于计算最终答案

首先看一下f[1][i]的min值 (QAQ虽然我也不知道这个是为什么)

然后dfs进行求解

啊,其实最后这个求解的部分,我至今都不是太理解QWQ

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath> using namespace std; inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
} const int maxn = 1010; struct Node{
int x,id;
};
int f[maxn][maxn];
int pos[maxn];
int n;
int pre[maxn];
Node a[maxn];
int cnt; bool cmp1(Node a,Node b)
{
return a.x>b.x;
} bool cmp2(Node a,Node b)
{
return a.id<b.id;
} int count(int p,int x)
{
int cnt=0;
for (int i=1;i<p;i++)
{
if (a[i].x<x) cnt++;
}
return cnt+1;
} void dfs(int x,int now)
{
if (x==n) return;
if (pos[x]!=now)
{
cnt++;
dfs(x+1,now);
}
else
dfs(x+1,pre[x]);
//cout<<1;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
a[i].x=read();
a[i].id=i;
}
sort(a+1,a+1+n,cmp1);
for (int i=1;i<=n;i++){
pos[i]=a[i].id;
a[i].x=i;
}
sort(a+1,a+1+n,cmp2);
++n;
a[n].x=n;
memset(f,127/3,sizeof(f));
f[n][n]=0;
for (int i=n-1;i>=1;i--)
{
int aa=count(pos[i],i);
int bb=1;
for(int j=1;j<=pos[i]-1;j++)
{
if (a[j].x<i+1) bb++;
if (f[i+1][j]<f[1005][1005]) f[i][j]=f[i+1][j]+aa+bb; //之所以不用减1是因为此时i的位置是aa+1并非aa 然后+1和-1正好抵消了
}
bb++;
for (int j=pos[i]+1;j<=n;j++)
{
if (a[j].x<i+1) bb++;
if (f[i+1][j]<f[1005][1005]) f[i][j]=f[i+1][j]+aa+bb-1;
}
int tmp =0;
for (int j=pos[i]+1;j<=n;j++)
{
if (f[i][pos[i]]>f[i+1][j]+tmp){
f[i][pos[i]]=f[i+1][j]+tmp;
pre[i]=j;
}
if (a[j].x<i) tmp+=i-a[j].x;
}
}
int ans=1e9;
int poss=0;
for (int i=1;i<=n;i++)
{
if (f[1][i]<ans){
ans=f[1][i];
poss=i;
}
}
dfs(1,poss);
cout<<cnt;
return 0;
}

bzoj1341 名次排序问题rank sorting(dp,考虑到对未来的贡献)的更多相关文章

  1. 题解 Sue的小球/名次排序问题/方块消除/奥运物流

    Sue的小球 名次排序问题 方块消除 奥运物流 Sue的小球 题目大意 有 \(n\) 个小球在下落,初始位置 \((x_i,y_i)\),下落速度为 \(v_i\).你初始位置在 \(x_0\),速 ...

  2. D. Sequence Sorting dp

    D. Sequence Sorting 题目大意:给你一个序列,有一种操作就是对所有相同的数可以挪到最前面,也可以挪到最后面,问最少操作次数. 首先,对于很多个相同的数,可以缩成两个位置,一个是就是这 ...

  3. bzoj2037 Sue的小球(区间dp,考虑到对未来的贡献)

    ​​​​​​​​​​​​​​大致意思就是现在你要不断的奔跑到不同的地点去接球,每一秒可以移动一个单位长度,而你接到一个球的动作是瞬间的,收益是y[i]-t*v[i] 然后呢,要求分数最高. 起初看这个 ...

  4. poj1417(种类并查集+dp)

    题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示 ...

  5. LightOJ1326 Race(DP)

    题目问N匹马比赛有多少种结果.一开始想用排列组合搞搞,然后发现想错了.艰难地把思路转向DP,最后AC了. dp[i][j]表示前i匹马确定出j个名次的方案数 dp[1][1]=1 对于第i匹马,它要确 ...

  6. mysql 累加排序求名次

    自己做的一个小项目里需要用mysql计算一些信息. mysql中的 表如下(注:表中数据都是测试数据,随机生成的) mysql> select * from shake_log; +-----+ ...

  7. CF766 E. Mahmoud and a xor trip [预处理][树形dp]

    题解: 二营长!你他娘的意大利炮呢? dp[i][j][0]: 从i,跋涉到以i为根的子树的每一个节点,在第j个数位上一共产生了多少个0. dp[i][j][1]: 从i,跋涉到以i为根的子树的每一个 ...

  8. [SDOI2013]淘金 数位DP

    做了好久.... 大致思路: 求出前k大的方格之和即为答案, 先考虑一维的情况,设f[i]为数位上各个数相乘为i的数的总数,也就是对于数i,有f[i]个数它们各个位相乘为i, 再拓展到二维,根据乘法原 ...

  9. Codeforces 1142D Foreigner (DP)

    题意:首先定义了一种类数(标志数) 1:1到9都是标志数. 2:若x / 10是标志数,假设x /10在标志数中的排名是k, 若x的个位数小于k % 11, 那么x也是标志数. 现在给你一个字符串,问 ...

随机推荐

  1. @ModelAttribute 与@InitBinder

    3.4.6 @ModelAttribute 注解 Mod lAttribut 通常作用在 Controller 的某个方法上,此方法会首先被调用, 井将方法 结果作为 Model 的属性 然后再调用对 ...

  2. 【Office Excel】vlookup函数的反向查找实例教程,不只是正向查找,还可以反向查找,实例讲解

    VLOOKUP 反向查询 众所周知,vlookup只能从左向右查找,而不能从右至左的反向查找.为此高手们设计了一个让无数新手迷惑的公式.今天优爱酷将彻底帮同学们解开这个迷团. [例]如下图所示要求根据 ...

  3. 面试必备:排序算法汇总(c++实现)

    排序算法主要考点: 7种排序 冒泡排序.选择排序.插入排序.shell排序.堆排序.快速排序.归并排序 以上排序算法是面试官经常会问到的算法,至于其他排序比如基数排序等等,这里不列举. 以下算法通过c ...

  4. 如何获取 Android CPU 核心数 (Java/C++)

    1 前言 最近学习Power HAL方面相关知识,透过Power HAL 去配置CPU的Freq需要先确定 CPU 核数.便先了解如何获取 Android CPU 核数. 2 Java层获取方式 // ...

  5. eBPF 安全项目 Tracee 初探

    1. Tracee 介绍 1.1 Tracee 介绍 Tracee 是一个用 于 Linux 的运行时安全和取证工具.它使用 Linux eBPF 技术在运行时跟踪系统和应用程序,并分析收集的事件以检 ...

  6. Qt 程序发布以及打包成exe安装包

    一.简述 Qt 项目开发完成之后,需要打包发布程序,而因为用户电脑上没有 Qt 配置环境,所以需要将 release 生成的 exe 文件和所依赖的 dll 文件复制到一个文件夹中,然后再用 Inno ...

  7. 开源自己编写的半人工标注平台PaddleOCRLabel(.NET Winform版本)

    大家好, 我是博客园的老用户了,许久不做.NET技术了,从2013年起,开始从事App技术,写过书,在Linux上搭建区块链,用GO写智能合约,使用nodejs搭建过微服务,用python写过爬虫,写 ...

  8. shell编程之条件语句

    目录: 一.条件测试 1.test命令测试 2.文件测试 3.字符串比较 4.逻辑测试 二.if语句 1.if单分支语句 2.if双分支语句 3.if多分支语句 三.case语句 case多分支语句 ...

  9. unity2021游戏引擎安装激活并汉化

    今天重新搭建了下unity的开发环境,也踩了不少坑,还有就是看了一些unity3d的教程,越看越不可思议,unity居然能做这么多好玩的东西,像枪战类,模拟类,角色扮演,动作冒险都很震撼. 但是震撼归 ...

  10. git各种操作:基本操作 and 多人协作 and 冲突解决

    git基本操作 git 上次文件到远程仓库(参考:https://blog.csdn.net/beiqiaofeng123/article/details/104859326) 如果第一次上传,配置一 ...