Given a sequence A_1, A_2, \cdots, A_nA1,A2,⋯,An. As for a subsequence A_{b_1}, A_{b_2}, \cdots, A_{b_m} \, (1\le b_1 < b_2 < \cdots < b_m \le n)Ab1,Ab2,⋯,Abm(1≤b1<b2<⋯<bm≤n), we say it magical if and only if mm is even and A_{b_1} + A_{b_2} = A_{b_3} + A_{b_4} = \cdots = A_{b_{m-1}} + A_{b_m}Ab1+Ab2=Ab3+Ab4=⋯=Abm−1+Abm. Determine the maximum length among all magical subsequences of the given sequence.
题意:
给你一个原始序列a,让你找出一个最长的子序列b,使这个子序列满足条件b[1]+b[2]=b[3]+b[4]=......,输出这个子序列的长度。
思路:题干给出原始序列a的数据范围很小,1<=a[i]<=100,所以每两个数的和一定不超过200,可以从这里入手,从2到200枚举每一种两数之和的情况,然后再用dp计算每种情况下的最长的序列。
定义dp[i][j]表示到第j个数为止,两数之和为i的最长序列的长度。
定义st[i][j]表示第i个数往前数最近的数值为j的下标。
状态转移方程为dp[i][j]=max(dp[i][j],dp[i][st[j][i-a[j]]-1]+2);//注意这里必须减-1
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int dp[210][N];
int st[N][210];//st[i][j]表示离第i个数最近的值为j的下标
int main()
{
int n;
scanf("%d",&n);
int num[210];//num[i]表示当前情况下所有的值为i的数中最靠右的位置
int a[N];
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
for(int j=1;j<=100;j++)
st[i][j]=num[j];
num[a[i]]=i;
}
for(int i=2;i<=200;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=dp[i][j-1];
if(a[j]>=i)
continue;
if(st[j][i-a[j]])
dp[i][j]=max(dp[i][j],dp[i][st[j][i-a[j]]-1]+2);
}
}
int ans=0;
for(int i=2;i<=200;i++)
{
ans=max(ans,dp[i][n]);
}
cout<<ans<<endl;
return 0;
}