洛谷P1028 [NOIP2001 普及组] 数的计算 —— 简单DP+双指针优化

This way

题意:

给出自然数 n n n,要求按如下方式构造数列:

  1. 只有一个数字 n n n 的数列是一个合法的数列。
  2. 在一个合法的数列的末尾加入一个自然数,但是这个自然数不能超过该数列最后一项的一半,可以得到一个新的合法数列。

请你求出,一共有多少个合法的数列。两个合法数列 a , b a, b a,b 不同当且仅当两数列长度不同或存在一个正整数 i ≤ ∣ a ∣ i \leq |a| ia,使得 a i ≠ b i a_i \neq b_i ai=bi

题解:

    在搜索不确定时间复杂度的情况下,尽量避开。
    从前往后也可以,我这里用的是从后往前的,也就是从小的数开始每次加变大的。我们知道每次至少*2,所以最多也就增大10次。那么dp[i][j]表示到了第i个数,值为j的情况数。它可以从dp[i-1][1:j/2]转移过来。我们从小往大枚举j的话,就可以用一个sum维护i-1位置上,值≤j/2的所有情况和。

#include
using namespace std;
#define ll long long
const int N=1e3+5;
ll dp[15][N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n/2;i++)dp[1][i]=1;
    ll ans=1;
    for(int i=2;i<=10;i++){
        ll sum=0,pos=1;
        for(int j=1<<i-1;j<=n/2;j++){
            while(pos*2<=j)sum+=dp[i-1][pos++];
            dp[i][j]=sum;
        }
        while(pos*2<=n)sum+=dp[i-1][pos++];
        ans+=sum;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(dp,算法,动态规划,c++)