想(dp)

给出一个音符序列,求最长子序列,满足其由若干个低-高-中-高-(中-高-中-高……)组成。\(n \le 1e5\)

好题啊。类比最长不下降子序列的思想,考虑储存最后两位数,推出dp方程(用到了前缀和思想)。

这道题的启发性在于:

  • 对于\(有有f[i][j]=f[i-1][j]\)类转移的dp,用滚动数组可能会更舒适
  • 状态的定义不一定要统一。例如本题中的\(f[i][j][k][0-3]\),j和k的含义就根据后面的数字是几来决定。只要状态定义不重不漏,并且转移方程正确就行了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=1e5+5, INF=0x3f3f3f3f, maxm=100;
int n, a[maxn], m, max3;
int f0[maxm][maxm], f1[maxm][maxm], f2[maxm][maxm], f3[maxm][maxm];
void up(int &x, int y){ if (x<y) x=y; }

int main(){
    scanf("%d", &n); max3=0;
    for (int i=1; i<=n; i++) scanf("%d",&a[i]), m=max(a[i], m);
    memset(f0,-INF, sizeof(f0)); memset(f1, -INF, sizeof(f1));
    memset(f2, -INF, sizeof(f2)); memset(f3,-INF, sizeof(f3)); 
    for (int i=1; i<=n; i++){
        int x=a[i];
        for (int j=x+1; j<=m; j++) up(f0[x][j], max3+1);
        for (int j=1; j<x; j++) up(f1[j][x], f0[j][x]+1);
        for (int j=1; j<=m; j++) up(f1[j][x], f1[j-1][x]);
        for (int j=x+1; j<=m; j++) up(f2[x][j], max(f1[x-1][j], f3[x][j])+1);
        for (int j=1; j<x; j++) 
            up(f3[j][x], f2[j][x]+1), up(max3, f3[j][x]);
    }
    printf("%d\n",max3);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/MyNameIsPc/p/9571413.html
今日推荐