CodeForces 813D Two Melodies dp,思维,好题

CodeForces 813D

题意:给出长度为 n 的序列,要找出两个非空的子序列,这两个子序列要满足:相邻两个元素相差 1 或相差 7 的倍数。 问两个子序列的长度和的最大值。

tags:容易想到 dp[i][j] 表示选择两个子序列分别以第 i 个元素和第 j 个元素结尾的最大长度和 。但难在怎么转移。

设 i < j , k<j, 则 dp[i][j] 只能从 dp[i][k] 转移,而不能从 dp[k][j] 转移,因为我们不能保证第二个子序列是否用到第 k 个元素。

然后用数组 maxmod[i] 维护 ai%7 == i 的最大dp[i][j],  maxnum[i] 维护 ai == i 的最大的 dp[i][j] 。

我们只要固定一个 i, 对于每个 i ,初始化一遍 maxmod[] 和 maxnum[], 然后枚举 j 从 i+1~n , 更新 dp[i][j] 即可。

dp[i][j] 有可能从 a[j]+1、a[j]-1、maxmod[a[j]%7] 或 dp[i][0] (即第 j 个元素新开一个序列)  转移而来, 在转移的同时更新 maxmod[] 和 maxnum[] 。

另外, dp[j][i] 要由 dp[i][j] 得到,这点容易忽略。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 5005;

int n, a[N], dp[N][N], maxmod[10], maxnum[100005];
int main()
{
    scanf("%d", &n);
    rep(i,1,n) scanf("%d", &a[i]);
    int ans=0;
    rep(i,0,n)
    {
        mes(maxmod, 0);
        mes(maxnum, 0);
        rep(j,0,i)
        {
            maxmod[a[j]%7] = max(maxmod[a[j]%7], dp[i][j]);
            maxnum[a[j]] = max(maxnum[a[j]], dp[i][j]);
        }
        rep(j,i+1,n)
        {
            dp[i][j] = max(maxnum[a[j]+1]+1, dp[i][j]);
            dp[i][j] = max(maxnum[a[j]-1]+1, dp[i][j]);
            dp[i][j] = max(maxmod[a[j]%7]+1, dp[i][j]);
            dp[i][j] = max(dp[i][0]+1, dp[i][j]);
            maxnum[a[j]] = max(maxnum[a[j]], dp[i][j]);
            maxmod[a[j]%7] = max(maxmod[a[j]%7], dp[i][j]);
            dp[j][i] = dp[i][j];
            ans = max(ans, dp[i][j]);
        }
    }
    printf("%d
", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/sbfhy/p/7704801.html