Codeforces813DTwo Melodies(dp)

链接

题意:

 给定一个长度为n的序列,需要取出两个不相交的子序列,每个子序列满足相邻两个数相差一或者求余7相等。求满足条件的两个不相交的子序列长度的和最大可以是多少(\(n<=5000\),\(a[i]<=1e5\))

分析:

 考虑dp的做法,\(dp[i][j]\)表示子序列\(1\)\(a[i]\)结尾,子序列\(2\)\(a[j]\)结尾时能得到的最大值。从0开始枚举\(i\)(0就代表只有一个序列)则有\(dp[i][j]=max(dp[i][k]+1,dp[i][j])\) \((k<j,a[k]≡a[j]mod7||abs(a[k]-a[j])=1)\)

 注意在枚举\(j\)时需要从\(i+1\)开始枚举这样可以避免序列1和序列2选到同一个元素。这样的做法是\(O(n^3)\),显然会T,由于a[i]<=1e5,所以可以用\(1e5\)的数组存放值为\(a[k]\)\(dp[i][k]\)的最大值,因此可以将复杂度降为\(O(n*1e5)\).

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=5005;
int a[maxn];
int dp[maxn][maxn];
int val[100005];
int mod[8];
int main()
{
	int n;
	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 = 1;j < i;++j){
			mod[a[j]%7]=max(mod[a[j]%7],dp[i][j]);
			val[a[j]]=max(val[a[j]],dp[i][j]);
		}
		for(int j = i+1;j <= n;++j)
		{
			dp[i][j]=dp[i][0]+1;
			dp[i][j]=max(dp[i][j],mod[a[j]%7]+1);
			dp[i][j]=max(dp[i][j],val[a[j]-1]+1);
			dp[i][j]=max(dp[i][j],val[a[j]+1]+1);
			mod[a[j]%7]=max(mod[a[j]%7],dp[i][j]);
			val[a[j]]=max(val[a[j]],dp[i][j]);
			dp[j][i]=dp[i][j];//显然dp[i][j]和dp[j][i]是等价的
			ans=max(ans,dp[i][j]);
		}
		memset(val,0,sizeof(val));
		memset(mod,0,sizeof(mod));
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-11-12 21:15  tryatry  阅读(115)  评论(0编辑  收藏  举报