bzoj1831: [AHOI2008]逆序对 DP

67 篇文章 0 订阅

这套题里面就这个题没有一次A掉。。。40分还是不错的。

文章转自ZYF-ZYF

         这些填的数应该有什么性质---一列数能有什么性质?大概就是递增递减吧。。。
         (吐槽:这思路也太牵强了吧。。。回答:。。。。。。)
        下面我们考虑两个空 a,b ,分别填上了x,y (假设只有两个空,并且x>y)
         如果我们交换 x 和 y 那么会有这样几条性质:
         1.[1,a-1],[b+1,n]中的数与x,y构成的逆序对没有发生改变
         2.[a+1,b-1]中 >max(x,y)的数与x,y构成的逆序对没有发生改变
         3.[a+1,b-1]中 < min(x,y)的数与x,y构成的逆序对没有发生改变
         4.[a+1,b-1]中处于区间(x,y)的数不再与x,y构成逆序对
         5.x,y不再构成逆序对
         也就是说我们交换x,y得到的答案一定会减小!
         稍微推广一下就是 这些填的数单调递增,但不一定是严格的
         考虑实现
         for i=1 to n do
          for j=1 to k do
            for p=1 to j do
             f[i,j]=min(f[i,j],f[i-1,p]+cost(i,j))


         NO NO NO
         既然考虑到cost(i,j)是固定的,我们只需要求f[i-1,1],f[i-1,2]......f[i-1,j]的最小值即可
         前缀最小值优化!类似于前缀和。。。
         这样状态数一共有O(N*K)个,每个状态的转移的复杂度为O(1)

PS:其实就是预处理的时候把每一位填的数所具有的影响处理出来,这样代价就在O(1)的时间里得到了。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n,k,b[11000],tot,save[11000];
long long f[11000][110],big[11000][110],g[11000][110],sma[11000][110];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&save[i]);
        if(save[i]==-1) tot++,b[tot]=i;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++)
        {
            big[i][j]=big[i-1][j];
            if(save[i]>j) big[i][j]++;
        }
    }
    for(int i=n;i>=1;i--)
    {
        for(int j=1;j<=k;j++)
        {
            sma[i][j]=sma[i+1][j];
            if((save[i]<j)&&(save[i]!=-1)) sma[i][j]++;
        }
    }
    for(int i=1;i<=tot;i++)
    {
        f[i][1]=f[i-1][1]+big[b[i]][1]+sma[b[i]][1];
        g[i][1]=f[i][1];
        for(int j=2;j<=k;j++)
        {
            f[i][j]=g[i-1][j]+big[b[i]][j]+sma[b[i]][j];
            g[i][j]=min(g[i][j-1],f[i][j]);
        }
    }
    long long ans=10000000000;
    for(int i=1;i<=k;i++)
    {
        ans=min(ans,f[tot][i]);
    }
    for(int i=1;i<=n;i++) ans+=big[i][save[i]];
    printf("%lld",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值