codeforces 1350 B. Orac and Models

传送门
在这里插入图片描述
题目大意:找最长递增子列满足:1.最长 2.递增 3.后一个index能整除前一个index
分析:子列问题,一般两种思路,一个是尺取,一个是dp。
用dp。
先不管本题的第三个条件,我们考虑一般情况,即:求最长递增子列
设dp[i]数组,表示:以s[i]为起始的最长递增子列数目,首先初始化为1。
我们可以从数列的后面向前分析(向前递推),先给出转移方程:
dp[i]=max(dp[i],dp[j]+1)
解释:
i为当前位置,j为当前位置后面的位置,看下面这个例子。
如果后面有比自己大的a,那么比较一下自己的dp和那个人的dp加上1(为什么加1?正是因为自己比他小,那个人身上有的dp值加上自己这个小的)谁大谁小,取大。

i:		1 2 3 4 
a[i]:   1 4 2 3 
dp[i]:  1 1 1 1  (初始状态)

i=3      j=4
i:		1 2 3 4 
a[i]:   1 4 2 3 
dp[i]:  1 1 2 1 

i=2      j=3 4
i:		1 2 3 4 
a[i]:   1 4 2 3 
dp[i]:  1 1 2 1 

i=1     j=2 3 4
i:		1 2 3 4 
a[i]:   1 4 2 3 
dp[i]:  3 1 2 1 

(好啰嗦啊,反正就是那个公式,自己再研究研究这个例子)
跳到这个题,多了一个条件。那我们想,n/2前的数一定都有倍数,n/2后的数一定都没有倍数了。举个例子

n=5
i:1 2 3 4 5

n/2=2
那么我们可以看到1有倍数(1*21*31*41*5),2有倍数(2*2),3就没有了因为最小的3*2=6已经大于5了
所以我们循坏的时候,与上面的有一点点不同,只需改动一下将i从n开始改为从n/2开始 i--.同时,j向后找的时候,也不必一个一个的找,只需j+=i,自己悟吧
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<map>
#define ll long long 
using namespace std;
ll s[100000+100];
ll dp[100000+100];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ll n;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			scanf("%I64d",&s[i]);
			dp[i]=1;
		}
		ll ans=-1;
		for(int i=n/2;i>=1;i--)
		{
			for(int j=i;j<=n;j+=i)
			{
				if(s[j]>s[i])
					dp[i]=max(dp[i],dp[j]+1);
			}
			ans=max(dp[i],ans);
		}
		if(ans!=-1)
			cout<<ans<<endl;
		else
			cout<<1<<endl;
	}
	return 0;
}
posted @ 2020-07-10 16:00  DuJunlong  阅读(5)  评论(0编辑  收藏  举报  来源