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

文章描述了一个关于自然数数列构造的问题,其中数列的每个新增元素不能超过前一个元素的一半。解决方案是使用动态规划,从后往前构建数列,dp[i][j]表示到达第i个数且值为j的合法序列数量。通过迭代和维护前一状态的和来计算当前状态的答案,最终得出所有可能的合法数列数量。
摘要由CSDN通过智能技术生成

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<bits/stdc++.h>
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值