BZOJ 1831 逆序对
Description
小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远。好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在\(1 \sim K\)之间。如果有两个数\(A\)和\(B\),\(A\)在\(B\)左边且\(A\)大于\(B\),我们就称这两个数为一个“逆序对”。你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离是多少千米了。 比如说,\(4\;2\;1\;3\;3\)里面包含了\(5\)个逆序对:\((4, 2), (4, 1), (4, 3), (4, 3), (2, 1)\)。 可惜的是,由于年代久远,这些数字里有一部分已经模糊不清了,为了方便记录,小可可用“\(-1\)”表示它们。比如说,\(4\;2\;-1\;-1\;3\)可能原来是\(4\;2\;1\;3\;3\),也可能是\(4\;2\;4\;4\;3\),也可能是别的样子。 小可可希望知道,根据他们看清楚的这部分数字,能不能推断出这些数字里最少能有多少个逆序对。
Input
第一行两个正整数\(N\)和\(K\)。第二行\(N\)个整数,每个都是\(-1\)或是一个在\(1 \sim K\)之间的数。
Output
一个正整数,即这些数字里最少的逆序对个数。
Sample Input
5 4
4 2 -1 -1 3
Sample Output
4
HINT
\(4\;2\;4\;4\;3\)中有\(4\)个逆序对。当然,也存在其它方案得到\(4\)个逆序对。
数据范围:
\(100\%\)的数据中,\(N\le10000\),\(K\le100\)。
\(60\%\)的数据中,\(N\le100\)。
\(40\%\)的数据中,\(-1\)出现不超过两次。
一道很好的dp题。(难得这题自己能够做出来)
\(f_{i,j,k}\)表示考虑前\(i\)个\(-1\),前面\(i\)个数中大于\(j\)的有\(k\)个数字的最优解。
\(pre_{i,j}\)表示前\(i\)个\(-1\)中,已知的数字部分小于等于\(j\)的有几个;\(sum_{i}\)表示整个序列小于等于\(j\)的数字有几个。
dp方程比较复杂,我在代码里写注释。
inline void dp()
{
memset(f[0],0x7,sizeof(f[0]));
int i,j,k,p,q;
for (i = 1;i <= K;++i) f[0][i][0] = 0;
for (i = 1;i <= cnt;++i)
{
p = i&1,q = p^1;
for (j = 1;j <= K;++j)
for (k = 0;k <= i;++k) f[p][j][k] = inf;
for (j = 1;j <= K;++j)
for (k = 0;k < i;++k)
f[p][j][k] = f[q][j][k] + k + pre[i][K] - pre[i][j] + sum[j-1] - pre[i][j-1]; //第$i$位中填$j$之后的贡献
for (j = 1;j <= K;++j)
for (k = i-1;k >= 0;--k)
{
if (j + 1 <= K) f[p][j][k+1] = min(f[p][j][k+1],f[p][j+1][k]); //递推更新$f$值,枚举顺序并不能更改
if (j > 1)f[p][j][k] = min(f[p][j][k],f[p][j-1][k]);
}
for (j = K - 1;j >= 1;--j)
for (k = i;k >= 0;--k) f[p][j][k] = min(f[p][j][k],f[p][j+1][k]); //递推再次更新$f$值
}
}
贴一份完整的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define inf (1<<29)
#define maxn 10010
#define maxk 110
int n,K,ans,seq[maxn],f[2][maxk][maxn],tree[maxk];
int cnt,sum[maxk],pre[maxn][maxk];
inline int lowbit(int x) { return x & -x; }
inline void modify(int x) { for (;x <= K;x += lowbit(x)) tree[x]++; }
inline int calc(int x) { int ret = 0; for (;x;x -= lowbit(x)) ret += tree[x]; return ret; }
inline void dp()
{
memset(f[0],0x7,sizeof(f[0]));
int i,j,k,p,q;
for (i = 1;i <= K;++i) f[0][i][0] = 0;
for (i = 1;i <= cnt;++i)
{
p = i&1,q = p^1;
for (j = 1;j <= K;++j)
for (k = 0;k <= i;++k) f[p][j][k] = inf;
for (j = 1;j <= K;++j)
for (k = 0;k < i;++k)
f[p][j][k] = f[q][j][k] + k + pre[i][K] - pre[i][j] + sum[j-1] - pre[i][j-1];
for (j = 1;j <= K;++j)
for (k = i-1;k >= 0;--k)
{
if (j + 1 <= K) f[p][j][k+1] = min(f[p][j][k+1],f[p][j+1][k]);
if (j > 1)f[p][j][k] = min(f[p][j][k],f[p][j-1][k]);
}
for (j = K - 1;j >= 1;--j)
for (k = i;k >= 0;--k) f[p][j][k] = min(f[p][j][k],f[p][j+1][k]);
}
}
int main()
{
freopen("1831.in","r",stdin);
freopen("1831.out","w",stdout);
scanf("%d %d",&n,&K);
for (int i = 1;i <= n;++i)
{
scanf("%d",seq+i);
if (seq[i] == -1) cnt ++, memcpy(pre[cnt],sum,sizeof(sum));
else
{
sum[seq[i]]++;
ans += calc(K + 1-seq[i]-1);
modify(K + 1 -seq[i]);
}
}
for (int i = 1;i <= cnt;++i)
for (int j = 1;j <= K;++j) pre[i][j] += pre[i][j-1];
for (int i = 1;i <= K;++i) sum[i] += sum[i-1];
int ret = 0;
if (cnt)
{
dp(); ret = inf;
for (int i = 1;i <= K;i++)
for (int j = 0;j <= cnt;++j)
ret = min(ret,f[cnt&1][i][j]);
}
printf("%d",ret + ans);
fclose(stdin); fclose(stdout);
return 0;
}
BZOJ 1831 逆序对的更多相关文章
- [BZOJ] 2431 逆序对数列
Time Limit: 5 Sec Memory Limit: 128 MB Submit: 2611 Solved: 1526 [Submit][Status][Discuss] Descripti ...
- BZOJ 2431 逆序对数列 DP
2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec Memory Limit: 128 MB Description 对于一个数列{ai},如果有i< j且ai> ...
- 【BZOJ】1831: [AHOI2008]逆序对
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1831 考虑$-1$的位置上填写的数字一定是不降的. 令${f[i][j]}$表示$DP$到 ...
- 「BZOJ 1831」「AHOI 2008」逆序对「贪心」
题意 给定一个长度为\(n\),值域为\([1,k]\),某些位置不确定的数组,求最小的逆序对.\(n\leq 10^4, k \leq 100\) 题解 这题有人用前缀和优化\(dp\)过了,但是这 ...
- BZOJ 1831: [AHOI2008]逆序对
题目大意: 给出一个序列,有几个位置上的数字任意.求最小的逆序对数. 题解: 自己决定放置的数一定是单调不降的.不然把任意两个交换一下就能证明一定会增加逆序对. 然后就可以DP了,f[i][j]表示第 ...
- Bzoj 2789: [Poi2012]Letters 树状数组,逆序对
2789: [Poi2012]Letters Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 278 Solved: 185[Submit][Stat ...
- 【刷题】BZOJ 3295 [Cqoi2011]动态逆序对
Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...
- BZOJ 3295 动态逆序对 | CDQ分治
BZOJ 3295 动态逆序对 这道题和三维偏序很类似.某个元素加入后产生的贡献 = time更小.pos更小.val更大的元素个数 + time更小.pos更大.val更小的元素个数. 分别用类似C ...
- bzoj 3744 Gty的妹子序列 区间逆序对数(在线) 分块
题目链接 题意 给定\(n\)个数,\(q\)个询问,每次询问\([l,r]\)区间内的逆序对数. 强制在线. 思路 参考:http://www.cnblogs.com/candy99/p/65795 ...
随机推荐
- Android(java)学习笔记183:判断SD卡状态和存储空间大小
当我们在使用SD卡时候,如果我们想往SD卡里读写数据,我们必须在这之前进行一个逻辑判断,那就是判断SD卡状态和SD存储空间大小: 核心代码: String status = Environment.g ...
- [转载]js中__proto__和prototype的区别和关系
首先,要明确几个点:1.在JS里,万物皆对象.方法(Function)是对象,方法的原型(Function.prototype)是对象.因此,它们都会具有对象共有的特点.即:对象具有属性_ ...
- JOSN对象与JSON字符串的相互转化
前端在与服务端通信时.会与JSON JSON字符串经常打交道. 什么是JSON? 如果不太清楚可以点击这里 服务端传过来的数据一般都是一串字符串,jQuery AJAX中的success函数的第一个 ...
- 求职,找工作,平台大PK
国内 猎聘网:www.lietou.com 拉钩网:Lagou.com 智联招聘:www.zhaopin.com 前程无忧:http://www.51job.com/ 中华英才网:chinahr.co ...
- CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\d29b5393\123c3a1c\App_Code.odl3w4o6.dll”--“拒绝访问。 ”
IIS部署网站或者Webservice时,出现以下问题: CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Tempor ...
- C#中方法的参数修饰符
做项目久了,有的时候真的需要静下心来认真的总结一下自己所用到的技术,而不是每天依葫芦画瓢,每天忙忙碌碌,到头来不知道自己忙了个啥,学了什么,自己到底掌握了多少知识.所以我想回顾一下C#的基础知识,把重 ...
- SOCKET,TCP/UDP,HTTP,FTP
(一)TCP/UDP,SOCKET,HTTP,FTP简析 TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层: 网络层:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议 传 ...
- jvm - 内存结构以其解析
可以将jvm粗略分为以下部分: Heap Memory:存储java对象. Non-Heap Memory:存储加载的class文件,以及其他meta-data信息. Other:存储java代码,j ...
- 使用Navicat for Oracle 出现的错误
错误提示: 意思是不能创建oci环境我们需要对 Navicat for Oracle 做一下配置依此选择 Tools -> Miscellaneous -> OCI OCI library ...
- .NET下的加密解密大全(1): 哈希加密
.NET有丰富的加密解密API库供我们使用,本博文总结了.NET下的Hash散列算法,并制作成简单的DEMO,希望能对大家有所帮助. MD5[csharp]using System; using Sy ...