泥豪!我是2789617221guo!欢迎来到我的博客,你|

2789617221guo

园龄:1个月粉丝:2关注:2

2025-02-05 17:30阅读: 10评论: 0推荐: 0

洛谷P1028 [NOIP 2001 普及组] 数的计算 题解

P1028 [NOIP 2001 普及组] 数的计算

题目描述

给出正整数 \(n\),要求按如下方式构造数列:

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

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

输入格式

输入只有一行一个整数,表示 \(n\)

输出格式

输出一行一个整数,表示合法的数列个数。

样例 #1

样例输入 #1

6

样例输出 #1

6

提示

样例 1 解释

满足条件的数列为:

  • \(6\)
  • \(6, 1\)
  • \(6, 2\)
  • \(6, 3\)
  • \(6, 2, 1\)
  • \(6, 3, 1\)

数据规模与约定

对于全部的测试点,保证 \(1 \leq n \leq 10^3\)

题解

思路

可能同学们一开始会想到使用DFS搜索求解,但是交上去后无法拿到满分,很多测试点会TLE。对此,我们可以采取递归的方法来写题。

通过手动列举前几个\(n\)值所对应的结果,找出规律。我们发现,当\(n\)是一个奇数时,\(n/2\)\((n-1)/2\)的结果是一样的,即当\(n\)为奇数时,可以转换成求\(n-1\)的结果。于是我们列出了第一个递推式:

\[f(n)=f(n-1) (当n\mod2=1时) \]

随后,通过构造合法数列的方法,我们可以知道,求\(n\)的结果是所有对于\(1\le i\le n\div 2\)的正整数\(i\)\(f(i)\)的和加1(因为要包括不在后面加数的单独一个\(n\)的情况),递推式2:

\[f(n)=(\sum_{i=1}^{n\div 2}{f(i)})+1 \]

接下来我们考虑\(f\)的边界条件,当\(n=0\)\(n=1\)时,只有1个合法数列,所以返回1。

我们就顺利得出了下列递推式:

\[\begin{equation} \begin{cases} f(n)=f(n-1),当n\mod2=1 \\ f(n)=f(n)=(\sum\limits_{i=1}^{n\div 2}{f(i)})+1,当n\mod2=0 \\ f(n)=1,当0\le n\le 1 \end{cases} \end{equation} \]

代码

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int n, vis[1005];
int fun(int x) {
if(vis[x]) return vis[x]; // 如果这个问题已经求过了,则直接返回结果
if(x <= 1) return 1; //递推式3,当n<=1,返回1
if(x % 2 == 1) return fun(x - 1); // 递推式1,当n%2==1,返回f(n-1)
int ans = 1;
for(int i = 1; i <= x / 2; i++) { // 递推式2,当n%2==0,返回f(i)(1<=i<=n/2)的和
int s = fun(i);
vis[i] = s;
ans += s;
}
return ans;
}
int main() {
cin >> n;
cout << fun(n) << endl;
return 0;
}

本文作者:2789617221guo

本文链接:https://www.cnblogs.com/2789617221guo/p/18699863

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   2789617221guo  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起