洛谷P2362做题总结

题目链接

1 解题思路

1.1 定义的变量

· 数组h:存储n根木桩的高度
· 数组f:存储每个木桩的最长升序序列的长度
· 数组cs:存储每个木桩的最长升序序列的方案数
· 变量ti:存储比较后最长升序序列的木桩对应的下标

1.2 过程分析

对于h数组中的每个元素hj,做以下操作:
将h[i]和h[1]至h[j-1]之间的元素(下面将这些元素记作h[j])进行比较:
– 若h[k]<=h[j]:
– 判断加入h[i]后,h[i]的最长升子序列是否达到最长,即f[j]+1>f[i],如果是,更新f[i]。并且此时f[i]的方案数cs[i]更新为f[j]的方案数,即 cs[j]=cs[k]。
– 若f[k]+1==f[j],说明第j个元素的最长升子序列加入h[i]后的序列长度可以也达到f[i],又有新的方案,那就cs[j]需要加上cs[k]。
–之后比较一下更新后的方案数,是否大于全部方案中的最大方案数,是就记录下最大升子序列的下标,即ti=j。

2 AC代码

#include<bits/stdc++.h>
using namespace std;
int m; 
int h[200],f[200],cs[200];
int main(){
	cin>>m;
	for(int i=1;i<=m;i++){
		int x,ti=1;
		memset(h,0,sizeof(h));
		memset(f,0,sizeof(f));
		cin>>x;
		for(int j=1;j<=x;j++){
			cin>>h[j];
			f[j]=1;
			cs[j]=1;
		}
		for(int j=2;j<=x;j++){
			for(int k=1;k<j;k++){
				if(h[k]<=h[j]){
					if(f[k]+1>f[j]){
						f[j] = f[k]+1;
						cs[j]=cs[k];
					}
					else if(f[k]+1==f[j]){
						cs[j]+=cs[k];
					}
					if(f[k]+1>f[ti])
						ti=j;
				}
			}
		}
		cout<<f[ti]<<" "<<cs[ti]<<endl;
	}
    return 0;
}
<think>好的,我现在需要解决用户提到的P1970题目的解法或思路。首先,我得先回忆一下这道题的具体内容。P1970是一道关于调整花坛中花的顺序以形成波动序列的问题。题目要求通过调整花的高度,使得相邻的花严格递增或递减,形成一个波浪形排列,最终求出最长的可行序列长度。 用户可能已经尝试过一些方法但遇到了困难,比如提到的类差分法解题P3998时的TLE问题,所以需要更高效的思路。我需要先明确问题的关键点:如何高效地找到最长波动子序列,而不需要实际调整花的顺序。 首先想到的是动态规划(DP)方法,因为这类最长子序列问题通常可以用DP来解决。对于每个位置i,我们需要记录两种状态:一种是以i结尾且最后一步是上升的,另一种是以i结尾且最后一步是下降的。这样可以分别用两个数组up和down来存储。 接下来,我需要考虑状态转移方程。假设当前处理到第i朵花,高度为h[i]。对于up[i],如果h[i] > h[j],那么可以从down[j]转移过来,即up[i] = down[j] + 1。同样,down[i]则是当h[i] < h[j]时,从up[j]转移过来。这样,每次遍历前面的所有j,取最大值,时间复杂度是O(n²),对于n较大的情况可能不够高效。 不过,题目中的n范围可能允许O(n²)的解法,或者有没有更优的方法?比如贪心策略。例如,维护当前是上升还是下降的状态,每次尽可能延长序列。这种方法的时间复杂度是O(n),但需要正确判断何时切换状态。 例如,初始化时,第一个元素作为起点。然后从第二个元素开始,如果当前元素比前一个大,且当前处于下降状态,则切换为上升,长度加一;反之亦然。这种方法是否能覆盖所有情况?可能需要考虑多个波动的情况,比如连续的上升或下降是否需要调整。 不过,贪心方法可能无法处理所有情况,例如中间有多个小的波动。比如序列是1,3,2,4,1,这时候可能需要选择不同的转折点。因此,动态规划可能更可靠,尽管时间复杂度较高,但正确性有保障。 另外,用户提到的类差分法可能是指类似差分数组的处理,但不确定具体如何应用到这题。或许可以结合差分思想,记录变化点,从而减少状态数量。 现在需要确认题目的数据规模。比如,如果n是1e5,那么O(n²)的DP显然不行,需要更优的方法。但如果是较小的n,比如1e3,那么O(n²)是可行的。假设题目中的n较大,可能需要优化。 查阅P1970的原题,题目中的数据范围是n ≤ 1e5,所以必须使用O(n)或O(n log n)的算法。这时候动态规划显然不行,必须用贪心方法。 那如何用贪心策略处理这个问题?参考类似最长摆动子序列的问题,比如LeetCode 376. Wiggle Subsequence,其中最优解法是O(n)的贪心。 该问题的解法思路是:遍历数组,记录当前是上升还是下降的状态。当遇到符合下一个状态的变化时,延长序列长度,并切换状态。例如,如果之前是上升,那么需要找到下一个下降的点,反之亦然。这种方法可以在一次遍历中完成,时间复杂度为O(n)。 具体来说,维护两个变量:up和down,分别表示到当前位置为止,以上升或下降结尾的最长摆动子序列的长度。初始化时,up = down = 1。然后从第二个元素开始,比较当前元素与前一个元素的大小: - 如果当前元素 > 前一个元素,则up = down + 1; - 如果当前元素 < 前一个元素,则down = up + 1; - 如果相等,则不变化。 这样,最终结果就是max(up, down)。这种方法不需要实际构建序列,只记录状态的变化,因此时间复杂度是O(n),空间O(1)。 但需要注意,这种方法适用于严格摆动的情况,即相邻元素必须严格递增或递减。而题目中P1970的要求正是严格波动,所以适用。 因此,正确的解法应该是这种贪心策略,遍历一次数组,维护up和down两个变量,每次根据当前与前一个元素的关系更新它们。最终得到的最大值即为答案。 现在需要验证这个思路是否正确。例如,测试用例: 输入:1 2 3 4 5,期望输出2,因为只能选两个元素形成波动。但根据上述方法,初始up=1,down=1。第二个元素2>1,up=1+1=2,down=1。第三个元素3>2,此时比较up应为down+1,但down是1,所以up=2,保持不变。最终max(2,1)=2,正确。 另一个测试用例:1,3,2,4,1。正确的最长序列是1,3,2,4,1,长度5。根据贪心方法: 初始up=1, down=1. i=1(3>1): up=down+1=2, down=1. i=2(2<3): down=up+1=3, up=2. i=3(4>2): up=3+1=4, down=3. i=4(1<4): down=4+1=5, up=4. 最终max=5,正确。 所以该方法是可行的。 接下来需要将这种思路转化为代码。需要注意的是,题目中的花是一排的,不能改变顺序,所以直接遍历数组即可。 因此,代码的大致结构是: 读取n和数组h。 如果n <=1,直接输出n. 否则,初始化up=1, down=1. 遍历i从1到n-1: 如果h[i]>h[i-1],则up = down +1. elif h[i]<h[i-1],则 down=up+1. else: 不变. 最后输出max(up, down). 这样就能得到正确结果,并且时间复杂度是O(n),空间O(1),适用于1e5的数据规模。 用户提到的类差分法可能是指类似的处理方式,但可能在一些边界条件处理上出现了问题,导致TLE。而正确的贪心方法应该能通过所有测试点。 总结,该题的正确解法是贪心策略,维护两个状态变量,线性时间内解决问题。</think>### P1970 花匠 解法思路 **题目分析** 题目要求将花坛中的花调整为**严格交替上升下降**的波动序列,求最长的可行序列长度。核心问题等价于寻找数组中的**最长严格摆动子序列**。 --- #### 解法思路:贪心策略 通过维护两个状态变量动态追踪波动趋势,时间复杂度$O(n)$,空间复杂度$O(1)$。 1. **状态定义** - `up`:以当前元素结尾且最后一步**上升**的最长序列长度 - `down`:以当前元素结尾且最后一步**下降**的最长序列长度 2. **状态转移** - 若当前元素 > 前一个元素: $$ \text{up} = \text{down} + 1 $$ - 若当前元素 < 前一个元素: $$ \text{down} = \text{up} + 1 $$ - 相等时不更新状态(严格波动要求) 3. **最终结果** $$ \text{答案} = \max(\text{up}, \text{down}) $$ --- #### 参考代码 ```python n = int(input()) h = list(map(int, input().split())) if n <= 1: print(n) else: up, down = 1, 1 for i in range(1, n): if h[i] > h[i-1]: up = down + 1 elif h[i] < h[i-1]: down = up + 1 print(max(up, down)) ``` --- #### 关键点说明 - **贪心有效性**:每次波动变化时更新状态,保证最长序列的局部最优性[^1]。 - **严格波动处理**:仅当相邻元素严格不等时更新状态,符合题意要求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值