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*2,1*3,1*4,1*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;
}