洛谷习题P1028递推

递推问题必须搞清楚问题的递推公式

i
1					#1

2
	1				#2

3
	1				#2

4
4	2	1
	2	1
		1			#4
	
5
5	2	1
	2	1
		1			#4
	
6
6	3	1
	3	1
		
6	2	1
	2	1
		1			#6
		
7
7	3	1
	3	1
		
7	2	1
	2	1
		1			#6

显然可看出(当然,我第一次花了很久才懂)
if(i%2!=0)
f[i]=f[i-1];
else
f[i]=f[i-1]+f[i/2];

递推关系式一旦写出,问题就迎刃而解了

AC代码:

#include<bits/stdc++.h>
using namespace std;

int main(){
	int i,f[1001],n;
	f[0]=1;f[1]=1;
	cin>>n;
	
	for(i=2;i<=n;i++){
		if(i%2!=0){
			f[i]=f[i-1];
		}else{
			f[i]=f[i-1]+f[i/2];
		}
	}
	cout<<f[n];
}
<think>嗯,用户问的是洛谷P1028的C语言题解,我得先回忆一下这道题的具体内容。洛谷的题目编号P1028,应该是关于递归或动态规划的题目吧?可能涉及到数的计算或者某种递推关系。 首先,我需要确认题目的具体要求。洛谷P1028的题目名称可能是“数的计算”或者类似的。根据记忆,这道题的大意是说,给定一个自然数n,然后按照某种规则生成数列,直到不能生成新的数为止,求这样的数的个数。比如,规则可能是在左边加上一个不超过当前数一半的自然数,然后继续处理新数。例如,n=6时,可能的数列是6, 3, 1, 2, 1,但需要计算所有可能生成的数的总数,包括初始的n。 接下来,用户需要的是C语言的解法。我需要考虑如何用递归或者动态规划来解决这个问题。不过,递归可能会在较大的n时出现重复计算,导致效率低下,所以动态规划可能是更好的选择。 题目可能的递推关系式是,对于每个数i,它的方法数等于1加上从1到i/2的所有数的方法数之和。例如,f[i] = 1 + f[1] + f[2] + ... + f[i/2]。初始条件是f[0]=0或者f[1]=1。比如,当i=6时,i/2是3,所以f[6] =1 + f[1]+f[2]+f[3}。然后逐步计算到n的值。 接下来,我需要确定如何用C语言实现这个递推。可能需要一个数组来保存每个数的f值,然后通过循环从1到n计算每个f[i]。 例如,初始化数组f,其中f[0] = 0,f[1] = 1。然后对于每个i从2到n,计算i/2的整数部分,然后累加从1到该值的所有f[j]的值,加上1得到f[i]。 这样,时间复杂度是O(n^2)吗?比如,对于每个i,需要循环到i/2,所以总次数大约是n*(n/2)/2,可能对于n=1000来说,是可行的。而题目中的n范围可能不会太大,所以这样的方法应该可以通过。 然后,我需要验证这个递推关系是否正确。比如,当n=6时,计算f[6]: f[1] =1 f[2] =1 + f[1} =2 f[3] =1 +f[1}=2 f[6] =1 +f[1}+f[2}+f[3} =1+1+2+2=6,所以总共有6种数?或者这里的f[i]是否代表不同的数列数量?需要再仔细确认题目。 或者,题目中的每个数的生成方式可能只计算不同的数的个数,而不管路径如何。比如,当n=6时,生成的数是6、3、2、1,可能总共有4个数?这时候可能需要不同的递推方式。这个时候可能之前的理解有误。 这让我意识到可能之前的思路有问题,必须重新理解题目。用户可能在这里需要更准确的题解,所以必须确保递推式正确。 此时,我需要查找洛谷P1028的具体题目描述。假设题目是“数的计算”,其描述是:给定自然数n,构造数列从n开始,在左边添加一个不超过先前数一半的数,直到无法添加。例如,n=6时,可能的数列包括6、36、26、136、126、16,总共有6种不同的数列。所以题目要求的可能不是数的个数,而是不同的数列的数量。这时候,递推式应该对应的是不同的数列数目。 这时候,递推式应该是f(n) = 1 + f(1) + f(2) + ... + f(n/2),其中f(n)表示以n开头的数列数目。例如,当n=6时,n/2是3,所以可以添加1、2、3,每个对应的数列数目是f(1)、f(2)、f(3),再加上自己本身的情况(只有n自己一个数的情况)。 初始条件是f(0)=0,f(1)=1,因为1不能添加任何数,只能有自己。然后: f(2) =1 +f(1) =2 → 数列为2和12 f(3)=1 +f(1) =2 → 3和13 f(6)=1 +f(1)+f(2)+f(3)=1+1+2+2=6,符合例子中的结果。 所以正确的递推式应该是这样。因此,动态规划的方法是正确的。 现在,我需要用C语言实现这个递推式。创建一个数组dp,其中dp[i]表示i的数列数目。初始化dp数组,从1开始计算到n。对于每个i,从1到i/2累加dp[j],然后加1,得到dp[i]。 例如,C代码的大致结构如下: #include <stdio.h> int main() { int n; scanf("%d", &n); int dp[1001] = {0}; // 假设n最大为1000 dp[1] = 1; for (int i = 2; i <= n; i++) { dp[i] = 1; // 自己本身的情况 for (int j = 1; j <= i/2; j++) { dp[i] += dp[j]; } } printf("%d\n", dp[n]); return 0; } 这样是否正确?比如,当n=6时,输出6,符合样例。当n=3时,输出2。这样看来是正确的。 不过,需要注意的是,题目中的n的范围可能较大,比如n<=1000,这样的时间复杂度是O(n^2/2),对于n=1000来说,大约是500,000次循环,这在C语言中是可行的,不会超时。 但需要确认数组的大小是否足够。例如,题目中的n最大可能是1000,所以数组应该开成dp[1001]或者更大。 另外,初始条件是否正确?对于i=1,dp[1]=1,当i=2时,循环j=1到1,所以dp[2] =1 + dp[1}=2。是的,这正确。 所以这样的代码应该是正确的。用户可能需要这样的解法,所以应该给出这个动态规划的思路,并附上代码。 另外,可能需要注意,当i=0时的情况,但题目中的n是自然数,可能从1开始,所以不需要处理0的情况。 总结:这道题的关键是找到递推式,即每个数的结果等于前面所有可以添加的数对应的结果之和加1。然后用动态规划的方法计算即可。</think>洛谷 P1028 题目要求计算自然数 $n$ 按特定规则生成的所有数列数目。规则为:在数列左侧不断添加不超过当前数一半的自然数,直到无法添加为止。下面详细介绍解题思路及 C 语言实现。 --- ### **问题分析** 1. **规则理解** - 以 $n$ 为起点,每次在左侧添加一个 $\leq \frac{\text{当前数}}{2}$ 的自然数。 - 例如:$n=6$ 时,可生成数列:`6`、`3 6`、`2 6`、`1 3 6`、`1 2 6`、`1 6`,共 $6$ 种。 2. **递推关系** - 定义 $f[i]$ 表示以 $i$ 开头的数列总数。 - 递推式:$$f[i] = 1 + \sum_{k=1}^{\lfloor i/2 \rfloor} f[k]$$ 其中 $1$ 表示仅包含 $i$ 自身的数列,$\sum$ 表示添加左侧数后的所有可能。 --- ### **动态规划解法** 1. **初始化** - $f[1] = 1$(仅自身一种情况)。 2. **递推过程** - 从 $i=2$ 到 $n$,累加 $1$ 到 $\lfloor i/2 \rfloor$ 的 $f$ 值。 3. **时间复杂度** - $O(n^2)$,适用于 $n \leq 1000$。 --- ### **C 语言代码实现** ```c #include <stdio.h> int main() { int n; scanf("%d", &n); int dp[1001] = {0}; // 假设 n 最大为 1000 dp[1] = 1; // 初始化 f(1)=1 for (int i = 2; i <= n; i++) { dp[i] = 1; // 包含自身的情况 for (int j = 1; j <= i / 2; j++) { dp[i] += dp[j]; // 累加所有可能的左侧数对应的数列数 } } printf("%d\n", dp[n]); return 0; } ``` --- ### **代码说明** 1. **输入处理** - 使用 `scanf` 读取输入的 $n$。 2. **数组初始化** - `dp[1] = 1` 表示 $n=1$ 时只有一种情况。 3. **动态规划计算** - 外层循环遍历 $i=2$ 到 $n$。 - 内层循环累加 $1$ 到 $\lfloor i/2 \rfloor$ 的 `dp[j]`。 4. **结果输出** - 最终 `dp[n]` 即为答案。 --- ### **测试示例** - **输入:6** **输出:6** 符合题目给出的样例结果。 - **输入:3** **输出:2** 对应数列:`3`、`1 3`。 --- 通过动态规划递推,可高效解决该问题。此方法避免了递归的重复计算,适合处理较大的 $n$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值