2017多校四 1012题 hdu 6078 Wavel Sequence dp好题

本文解析了一道关于寻找两个数组间波浪形子序列的问题,通过动态规划的方法给出了O(n^2)的解决方案,并分享了从O(n^3)优化至O(n^2)的思考过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


题意:

给定两个数组 a 与 b,找到 a 中的一个子序列满足a1<a2>a3<a4>a5<a6...

并且在 b 中也有一个子序列与 a 一一对应,即  f1,f2,...,fk(1fin,fi<fi+1)  and  g1,g2,...,gk(1gim,gi<gi+1) , where  afi=bgi


参考:

http://blog.youkuaiyun.com/clx55555/article/details/76685981 ——但求-_-心安


思路:

考虑 sum[i][j][0/1] 代表 a 序列到 i 为止,b 序列到 j 为止,一共有多少个子序列符号要求,并且最后一个数字为波谷(0) / 波峰(1)

则有转移方程:

当 a[i] != b[j] 时,sum[i][j][0] = sum[i - 1][j][0], sum[i][j][1] = sum[i - 1][j][1], (即直接继承)

当 a[i] == b[j] 时,sum[i][j][0] = sum[i - 1][j][0] + sigma(sum[i - 1][k][1] | 1 <= k <= j - 1 && b[k] > b[j] = a[i])

     sum[i][j][1] = sum[i - 1][j][1] + sigma(sum[i - 1][k][0] | 1 <= k <= j - 1 && b[k] < b[j] = a[i])


乍一看,是个O(n^3)的转移方程,外层循环 i, 内层循环 j,转移时再循环 1 ~ j - 1,

事实上sigma里面的内容可以不用每次回头去算,而是可以随着 j 往后挪而同步的去计算。

为什么呢?可以观察到,sigma里面的式子限制条件事实上和 b[j] 并没有关系,而只是和 a[i] 有关系,这样一来,就可以通过当前的 a[i] 在 a[i] != b[j] 时去一点一点积累之后可能造成的影响了。

这一点是很关键的。这样一来,就变成了可以承受的O(n^2)了。


还有一个需要注意的地方,那就是要求波浪的第一个元素比第二个元素小。

所以对于单个元素,我们只能将其看作波谷,这样就能保证若它作为开头,那么它其后紧跟的元素只能比它大。


Code:

#include <bits/stdc++.h>
#define maxn 2010
#define mod 998244353
typedef long long LL;
int a[maxn], b[maxn];
LL sum[maxn][maxn][2];
void work() {
    memset(sum, 0, sizeof(sum));
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for (int i = 1; i <= m; ++i) scanf("%d", &b[i]);
    LL ans = 0;
    for (int i = 1; i <= n; ++i) {
        LL cntl = 1, cntg = 0;
        for (int j = 1; j <= m; ++j) {
            sum[i][j][0] = sum[i - 1][j][0]; sum[i][j][1] = sum[i - 1][j][1];
            if (a[i] == b[j]) {
                ans += (cntg + cntl) % mod; ans %= mod;
                sum[i][j][0] += cntl, sum[i][j][1] += cntg; sum[i][j][0] %= mod; sum[i][j][1] %= mod;
//                printf("%d %d %lld %lld\n", i, j, sum[i][j][0], sum[i][j][1]);
            }
            else if (a[i] > b[j]) cntg += sum[i - 1][j][0], cntg %= mod;
            else cntl += sum[i - 1][j][1], cntl %= mod;
        }
    }
    printf("%lld\n", ans);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


反思:

周四比赛时真的很颓...甚至都没有好好去想转移方程

哪怕想出来O(n^3)的不会优化也比什么努力都不去尝试好啊...不去尝试不去写怎么可能过呢

而且这真的是一道很好的题目

可以这样随意给我浪费的机会不多啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值