洛谷P2501数字序列 动态规划

题目

题目链接

题解

这道题目我觉得是神题,非常有意思的题目,也挺难做的。

第一问:求最少需要改变的数量

这一问比较简单,之前也见过这种套路。
定义 d p [ i ] 表示 a 序列中 a [ 1 , i ] 部分构成严格递增子序列且以 a [ i ] 为结尾所不需要改变的最大的数量。
那么转移方程就是:
d p [ i ] = m i n 1 j < i , a [ i ] a [ j ] >= i j ( d p [ j ] + 1 )
那么我们稍微对转移方程做一个变形:
d p [ i ] = m i n 1 j < i , a [ i ] i a [ j ] j ( d p [ j ] + 1 )
我们发现如果让 a [ i ] = i 的话,这就是一个最长非降子序列,我们可以使用二分的方法在 O ( n l o g n ) 的时间解决。
m x l e n 即是整个 a 序列能构成最长非降子序列的长度值。
那么第一问的答案就是 n m x l e n

在我们求解最长非减子序列的时候,我们需要处理出一个数组 f , f [ i ] 表示以以 a [ i ] 结尾的最长非减序列的长度。以及 m x l e n 个链表 n x t (我在代码里用vector表示的), n x t [ i ] [ j ] 表示满足 f [ x ] = i 的第 j 大的 x
用这些处理好的信息,我们就可以来做第二问了。

第二问:求改变的最小花费

在求改变的最小花费的时候我们仍然使用 d p 的方法来解决。
我们定义 d p 2 [ i ] 表示 a [ 1 , i ] 已经被修改成为最长非减子序列,并且末尾的 a [ i ] 没有被修改,所花费的最大值。

那么转移方程可以写成:
d p 2 [ i ] = m i n 0 j < i , f [ i ] = f [ j ] + 1 ( d p 2 [ j ] + c o s t ( j + 1 , i 1 ) )
这个 c o s t ( j + 1 , i 1 ) 表示将 a [ j + 1 , i 1 ] 中的所有元素都修改成为介于 [ a [ j ] , a [ i ] ] 之间的元素所花费的最小值。
我们来计算这个 c o s t ( j + 1 , i 1 )
首先,对于所有 a [ x ] 要么 > a [ i ] ,要么 < a [ j ]
因此我们所采取的最优策略就是找出一个分界点 k ,使得 a [ j , k ] 中的元素全都等于 a [ j ] 使得 a [ k + 1 , i ] 中的元素全部都等于 a [ i ] ,其中 j k < i
那么
c o s t ( j + 1 , i 1 ) = m i n j k < i ( j x k | a [ x ] a [ j ] | + k < x < i | a [ x ] a [ i ] | )
这样的话,我们枚举 i , j , k 就可以把这道题目在 O ( n 3 ) 的时间复杂度内解决掉。

下面我们看看能不能优化。

枚举 i 肯定不能省掉,对 i ,我们再开始枚举 j ,注意,我们直接在 n x t 链表中找到满足条件的 j ,而且我们枚举 j 的顺序是按照 j 从大到小的顺序,这样的话,我们可以顺手记录一个 s u m ,表示当前把 a [ j + 1 , i 1 ] 之间的元素全部都改变到 a [ i ] 了。即 s u m = j < x i | a [ x ] a [ i ] |
然后假设最优决策点是 k ,其中 j k < i 。再假设 a [ j + 1 , k ] 之间有 z 个小于 a [ i ] ,有 y 个大于 a [ i ]
那么 c o s t ( j + 1 , i 1 ) = s u m ( a [ i ] a [ j ] ) z + ( a [ i ] a [ j ] ) y
c o s t ( j + 1 , i 1 ) = s u m ( a [ i ] a [ j ] ) ( z y )
想要使得 c o s t 最小,必须让 z y 最大。

但实际上要求 j 位置开始的前缀的 z y 的最大值不好求,我们可以反过来想,当 i j 固定的时候, z y 的值也就固定了,我们可以通过求从 i 开始的后缀中 z y 的最小值。

这样这个题就可以在 O ( n 2 + n l o g n ) 的最差时间复杂度内解决掉了。

由于题目保证随机数据,所以 n 2 根本不可能跑满。

事实上还跑的很快。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5;
const ll inf = 0x3f3f3f3f;
typedef pair<int,ll> pii;
int n;
ll dp[maxn],a[maxn],f[maxn];
vector<int> nxt[maxn];
ll dp2[maxn];
int dfs(int u){
    if(dp2[u] != -1) return dp2[u];
    int ni = u;
    ll sum = 0,z = 0,y = 0;
    ll mizy = 0,res = 0;
    for(int i = nxt[f[u]-1].size()-1;i >= 0;--i){
        int nj = nxt[f[u]-1][i];
        if(nj > u || a[nj] > a[u]) continue;
        for(int j = ni;j > nj;--j){
            sum += abs(a[j] - a[u]);
            if(a[j] < a[u]) z++;
            if(a[j] > a[u]) y++;
            mizy = min(mizy,z-y);
        }
        ni = nj;
        int rr = sum - (a[u]-a[nj])*(z-y-mizy)+dfs(nj);
        if(!res || rr < res) res = rr;
    }
    return dp2[u] = res;
}
int main()
{
    memset(dp2,-1,sizeof(dp2));
    scanf("%d",&n);
    for(int i = 1;i <= n;++i) scanf("%lld",&a[i]),a[i] -= i;
    a[0] = -inf,a[n+1] = inf; 
    memset(dp,inf,sizeof(dp));
    for(int i = 1;i <= n;++i){
        int loc = upper_bound(dp+1,dp+1+n,a[i]) - dp;
        dp[loc] = a[i];
        f[i] = loc;
        nxt[f[i]].push_back(i);
    }
    int mxlen = lower_bound(dp+1,dp+1+n,inf) - dp - 1;
    cout<<n - mxlen<<endl;

    a[n+1] = inf;f[n+1] = mxlen+1;
    dp2[0] = 0;
    ll ans = dfs(n+1);
    cout<<ans<<endl;  
} 

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/80003607
今日推荐