Codeforces 813D Two Melodies(维护转移来源的DP)

34 篇文章 1 订阅

D. Two Melodies
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Alice is a beginner composer and now she is ready to create another masterpiece. And not even the single one but two at the same time!

Alice has a sheet with n notes written on it. She wants to take two such non-empty non-intersecting subsequences that both of them form a melody and sum of their lengths is maximal.

Subsequence is a sequence that can be derived from another sequence by deleting some elements without changing the order of the remaining elements.

Subsequence forms a melody when each two adjacent notes either differs by 1 or are congruent modulo 7.

You should write a program which will calculate maximum sum of lengths of such two non-empty non-intersecting subsequences that both of them form a melody.

Input
The first line contains one integer number n (2 ≤ n ≤ 5000).

The second line contains n integer numbers a1, a2, …, an (1 ≤ ai ≤ 105) — notes written on a sheet.

Output
Print maximum sum of lengths of such two non-empty non-intersecting subsequences that both of them form a melody.

Examples
input
4
1 2 4 5
output
4
input
6
62 22 60 61 48 49
output
5
Note
In the first example subsequences [1, 2] and [4, 5] give length 4 in total.

In the second example subsequences [62, 48, 49] and [60, 61] give length 5 in total. If you choose subsequence [62, 61] in the first place then the second melody will have maximum length 2, that gives the result of 4, which is not maximal.

题目大意

  有一个长度为 N(2N5000) 的序列,要求从中选择两个相互独立的子序列,每个子序列相邻元素之间差 1 或对于7同余,使得这两个字序列的长度之和最大。

解题思路

  首先很容易相当 dp[i][j] 表示两个字序列分别以 i,j 结尾的最长长度。转移就是枚举下一个位置,总时间复杂度为 O(N3) ,显然无法通过。
  考虑固定一个 i ,则对每个j来说,只要考虑 dp[i][k](k<j) 中同余的长度最大的,值小 1 的长度最大的,值大1的长度最大的。所以我我们可以对于每个 i ,维护每个同余值的长度最大值,和每个值的长度最大值,并在转移的过程中更新,O(1)的实现转移。总时间复杂度 O(N2)

AC代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <cmath>
#include <map>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXN=5000+3;
const int MAXA=100000+3;
int N, a[MAXN];
int dp[MAXN][MAXN];//表示一个序列以i结尾,一个序列以j结尾的最大长度
int max_mod[10], max_a[MAXA];//表示此时a[j]%7为各个值的最长序列,表示a[j]为各个值的最长序列

int main()
{
    scanf("%d", &N);
    for(int i=1;i<=N;++i)
        scanf("%d", &a[i]);
    int ans=0;
    for(int i=0;i<=N;++i)
    {
        for(int j=0;j<7;++j)
            max_mod[j]=0;
        for(int j=1;j<=N;++j)
            max_a[a[j]]=0;
        for(int j=1;j<i;++j)//预处理出此时各个情况的最大长度
        {
            max_mod[a[j]%7]=max(max_mod[a[j]%7], dp[i][j]);
            max_a[a[j]]=max(max_a[a[j]], dp[i][j]);
        }        
        for(int j=i+1;j<=N;++j)//进行转移并更新维护的值
        {
            dp[i][j]=max(max(max_a[a[j]-1], max_a[a[j]+1]), max(max_mod[a[j]%7], dp[i][0]))+1;
            dp[j][i]=dp[i][j];
            max_mod[a[j]%7]=max(max_mod[a[j]%7], dp[i][j]);
            max_a[a[j]]=max(max_a[a[j]], dp[i][j]);
            ans=max(ans, dp[i][j]);
        }

    }
    printf("%d\n", ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值