原链接
洛谷
Codeforces
题意简述
给定一个 n ( n < = 5000 ) n(n<=5000) n(n<=5000),以及 n n n个数 a 1 , a 2 , a 3 , ⋯ , a n ( a i < = 1 e 5 ) a_1,a_2,a_3,\cdots,a_n(a_i<=1e5) a1,a2,a3,⋯,an(ai<=1e5)定义一个 M e l o d y Melody Melody子序列(和"最长上升子序列"中的"子序列"定义一样,是指在原序列中相对顺序不变但是不一定连续的一段子序列)如下:
对于任意相邻的两个元素,一定满足这两个元素差为1或者两个元素除7同余。
请在原序列中找出两个无重复部分的 M e l o d y Melody Melody子序列并且选出的两个长度加起来最大。
(偷偷开心一下:这个盗的是洛谷上的翻译,它是我提供的)
数据
输入
第一行一个正整数
n
n
n
接下来一行有
n
n
n个正整数,顺序给出
a
1
,
a
2
⋯
a
n
a_1,a_2\cdots a_n
a1,a2⋯an
输出
如题。最长的两段不重复 M e l o d y Melody Melody序列的长度和。
样例
输入
4
1 2 4 5
输出
4
输入
6
62 22 60 61 48 49
输出
5
解释
解释:样例2中选出 62 , 48 , 49 62,48,49 62,48,49和 60 , 61 60,61 60,61这两个 M e l o d y Melody Melody子序列能使得总长度最大 = 5 ( 即2+3 ) =5(\text{即2+3}) =5(即2+3)
思路
很久以前,看到这个题,只看懂了题意,不会做。但是为了搞社区贡献就交了翻译,过了(事实证明没什么用)。
现在终于会做了,写篇题解纪念一下。。。
显然是个 d p dp dp吧。。。我们有一个朴素的 d p dp dp方程, d p [ i ] [ j ] dp[i][j] dp[i][j]表示第一段到 i i i,第二段到 j j j的最大长度和。显然,我们要找一个最大的 k k k满足 a [ k ] = a [ j ] ± 1 a[k]=a[j]\pm1 a[k]=a[j]±1或 a [ k ] ≡ a [ j ] ( m o d 7 ) a[k]≡a[j]\pmod 7 a[k]≡a[j](mod7)。此时用 d p [ i ] [ k ] + 1 dp[i][k]+1 dp[i][k]+1更新 d p [ i ] [ j ] dp[i][j] dp[i][j]。 d p [ i ] [ j ] dp[i][j] dp[i][j]初始值设为 d p [ i ] [ 0 ] + 1 dp[i][0]+1 dp[i][0]+1。
但是显然这个转移是 O ( n 3 ) O(n^3) O(n3)d的,过不去。所以我们想想如何优化。此时注意到 a i < = 1 e 5 a_i<=1e5 ai<=1e5,这告诉我们我们珂以把 a i a_i ai作为数组下标。设 m a x m [ x ] maxm[x] maxm[x]表示满足 a [ k ] ≡ x ( m o d 7 ) a[k]≡x \pmod 7 a[k]≡x(mod7)的 k k k中 d p [ i ] [ k ] dp[i][k] dp[i][k]最大的,注意 x ∈ [ 0 , 7 ) x\in[0,7) x∈[0,7)。 m a x n [ x ] maxn[x] maxn[x]表示满足 a [ k ] = x a[k]=x a[k]=x的 k k k中 d p [ i ] [ k ] dp[i][k] dp[i][k]最大的。当然,我们每次枚举 i i i的时候,都要计算一下这个 m a x m maxm maxm和 m a x n maxn maxn。除此之外,我们在一边计算 d p [ i ] [ j ] dp[i][j] dp[i][j]的时候也要记得一边更新 m a x m maxm maxm和 m a x n maxn maxn。
(Q:为什么叫
m
a
x
m
maxm
maxm和
m
a
x
n
maxn
maxn?)
(A:
m
m
m结尾表示
m
o
d
u
l
o
modulo
modulo,即模运算。
n
n
n结尾表示
n
u
m
b
e
r
number
number,即数值。洛谷上的M_sea巨佬称其为
m
a
x
m
o
d
maxmod
maxmod和
m
a
x
n
u
m
maxnum
maxnum。)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 5010
#define Val 100100
int n,a[N];
void Input()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
}
int dp[N][N];
int maxm[7],maxn[Val];
bool check(int a,int b) // 判断 a 和 b 是否能在原序列中相邻
{
if ((a-b)%7==0) return 1;
if (abs(a-b)==1) return 1;
return 0;
}
int max4(int a,int b,int c,int d)//四个取最值
{
return max(max(a,b),max(c,d));
}
int up(int &x,int y)//用y更新x的最大值
{
x=max(x,y);
}
void DP()
{
int ans=-1;
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;++i)
{
memset(maxn,0,sizeof(maxn));
memset(maxm,0,sizeof(maxm));
for(int j=1;j<=i;++j)
{
up(maxm[a[j]%7],dp[i][j]);
up(maxn[a[j]],dp[i][j]);
}// 转移方程
for(int j=i+1;j<=n;++j)
{
dp[i][j]=max4(dp[i][0],maxm[a[j]%7],maxn[a[j]-1],maxn[a[j]+1])+1;
// 讨论四种情况取最大值
up(maxm[a[j]%7],dp[i][j]);
up(maxn[a[j]],dp[i][j]);//记得更新maxm个maxn
up(ans,dp[i][j]);//更新答案
dp[j][i]=dp[i][j];///对称的,所以答案一样(这个上面忘了说了,不写就WA声一片了)
}
}
printf("%d\n",ans);
}
void Main()
{
Input();
DP();
}
};
main()
{
Flandle_Scarlet::Main();
return 0;
}
博客主要介绍了Codeforces 813D题目的解题思路,内容涉及动态规划和模运算优化。题目要求找到原序列中两个无重复部分的Melody子序列,使得它们的长度之和最大。博主分享了如何通过dp数组和优化技巧解决此问题,包括将数对模7进行处理以降低复杂度。
417

被折叠的 条评论
为什么被折叠?



