【BZOJ1831】[AHOI2008]逆序对(动态规划)
【BZOJ1831】[AHOI2008]逆序对(动态规划)
题面
题解
显然填入的数拎出来是不降的。
那么就可以直接大力\(dp\)。
设\(f[i][j]\)表示当前填到了\(i\),上一个填的数是\(j\)的最小逆序对数。
随便拿什么维护一下转移就好了。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 10010
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,K,sum,a[MAX],ans=1e9,f[MAX][101],s1[101],s2[101];
int main()
{
n=read();K=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=n;++i)if(~a[i])s2[a[i]]+=1;
for(int i=1;i<=K;++i)s2[i]+=s2[i-1];
for(int i=1;i<=K;++i)s1[i]=s2[i];
for(int i=1;i<=n;++i)
if(~a[i])
{
sum+=s1[a[i]-1];
for(int j=a[i];j<=K;++j)s1[j]-=1;
}
for(int i=1;i<=n;++i)
if(~a[i])
{
for(int j=1;j<=K;++j)f[i][j]=f[i-1][j];
for(int j=a[i];j<=K;++j)s2[j]-=1,s1[j]+=1;
}
else
{
for(int j=1;j<=K;++j)f[i][j]=f[i-1][j]+s2[j-1]+s1[K]-s1[j];
for(int j=1;j<K;++j)f[i][j+1]=min(f[i][j+1],f[i-1][j]+s2[j]+s1[K]-s1[j+1]);
for(int j=2;j<=K;++j)f[i][j]=min(f[i][j],f[i][j-1]);
}
for(int i=1;i<=K;++i)ans=min(ans,f[n][i]);
printf("%d\n",ans+sum);
return 0;
}