题目
Time Limit: 10 Sec
Memory Limit: 64 MB
Description
小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远。好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~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~K之间的数。
Output
一个正整数,即这些数字里最少的逆序对个数。
Sample Input
5 4
4 2 -1 -1 3
Sample Output
4
HINT
4 2 4 4 3中有4个逆序对。当然,也存在其它方案得到4个逆序对。
数据范围:
100%的数据中,N<=10000,K<=100。
60%的数据中,N<=100。
40%的数据中,-1出现不超过两次。
- 首先可以证明所填入的书应该是递增的。其实也没必要证来证去,肯定后面的大一点更好
- 所以我们想知道当第i个位置填的数是j时,上一个空位填1~j的情况,然后取最小,再加上此时会形成的逆序对数就行了
- 主要的思路太简单 >_>,看别人的博客吧
- 说一些实现的技巧:
- 可以预处理“此时会形成的逆序对数”
- 可以用一个数组记录“上一个空位填1~j的情况”
- 防止重复
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10010,K=110;
int n,k;
int a[N],tot,pos[N];
int c1[N][K],c2[N][K];
int f[N][K],g[N][K];
int ans=0x3f3f3f3f;
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
if(a[i]==-1){
tot++;
pos[tot]=i;
}
}
for(int i=1;i<=n;++i)
for(int j=1;j<=k;++j){
c1[i][j]=c1[i-1][j];
if(a[i]>j) c1[i][j]++;
}
for(int i=n;i>=1;--i)
for(int j=1;j<=k;++j){
c2[i][j]=c2[i+1][j];
if(a[i]<j&&a[i]!=-1) c2[i][j]++;
}
for(int i=1;i<=tot;++i){
f[i][1]=g[i-1][1]+c1[pos[i]][1]+c2[pos[i]][1];
g[i][1]=f[i][1];
for(int j=2;j<=k;++j){
f[i][j]=g[i-1][j]+c1[pos[i]][j]+c2[pos[i]][j];
g[i][j]=min(g[i][j-1],f[i][j]);
}
}
for(int i=1;i<=k;++i)
ans=min(ans,f[tot][i]);
for(int i=1;i<=n;++i)
if(a[i]!=-1)
ans+=c1[i][a[i]];
cout<<ans;
return 0;
}