洛谷P3395做题总结

题目链接

代码1:

1.1 解题思路

在存储上,开了两个二维数组vis和mp。vis数组用来标记格子是否访问过,为了简化边界判断,在初始化阶段,就把横纵坐标为0或n+1的格子标记为1。mp数组用来标记格子被摆路障的时间(步数),一开始将所有格子的值初始化为一个较大的数,之后根据输入的2n-2行对特定格子做标记。我们还要定义一个结构体pos,存储每个格子的横坐标、纵坐标和被访问的时间(步数)。之后使用BFS算法遍历队头元素上下左右的格子,如果vis标记为0,并且mp值大于当前已经走过的步数值,那么可以走该格子。判断了可以走之后,注意检查是否到了(n,n),如果是结束,输出”Yes”,否则创建pos对象入队,将vis标记为1,step值为当前值+1,反复这样做,知道队列为0.

1.2 解题反思

要对输入为1的情况做特判。

1.3 AC代码

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

struct pos {
	int x;
	int y;
	int step;
};

int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int vis[1005][1005];
int mp[1005][1005];

int t, n;

void init() {
	for (int j = 1; j <= n; j++) {
		for (int k = 1; k <= n; k++)
			mp[j][k] = 0x7fff;
	}
	for (int j = 1; j <= n; j++) {
		for (int k = 1; k <= n; k++)
			vis[j][k] = 0;
	}
	for (int j = 0; j <= n + 1; j++) {
		vis[0][j] = 1;
		vis[n + 1][j] = 1;
		vis[j][0] = 1;
		vis[n + 1][j] = 1;
	}

}
int main() {
	cin >> t;
	for (int i = 1; i <= t; i++) {
		cin >> n;
		if (n == 1) {
			cout << "Yes" << endl;
			continue;
		}
		init();
		for (int j = 1; j <= 2 * n - 2; j++) {
			int x, y;
			cin >> x >> y;
			mp[x][y] = j;
		}

		bool flag = 1;
		queue<pos> que;
		pos head;
		vis[1][1] = 1;
		head.x = 1;
		head.y = 1;
		head.step = 0;
		que.push(head);
		while (que.size() > 0) {
			pos cur = que.front();
			que.pop();
			for (int i = 0; i < 4; i++) {
				int tx = cur.x + d[i][0];
				int ty = cur.y + d[i][1];
				int tstep = cur.step;
				if (vis[tx][ty] == 0 && mp[tx][ty] >= tstep) {
					if (tx == n && ty == n) {
						cout << "Yes" << endl;
						flag = 0;
						break;
					}
					pos next;
					next.x = tx;
					next.y = ty;
					next.step = tstep + 1;
					vis[tx][ty] = 1;
					que.push(next);
				}
			}
			if (flag == 0)
				break;
		}
		if (flag == 1)
			cout << "No" << endl;
	}
	return 0;
}

代码2:

2.1 解题思路

在存储上开mp数组,标记格子是否被访问过,如果访问过则标记为1。开一个变量step记录步数,根据当前步数标记路障。这里还需要开数组sx和sy记录每秒设路障的横坐标和纵坐标。使用BFS算法求解,如果格子mp值为0可以走,可以走的情况下再看看是否到达(n,n),如果是则停止,输出“Yes",否则让格子入队,当队列为空时,结束循环。

2.2 解题反思

1.BFS可以用来统计到达目的地最少的步数,当各个方向都判断了合法性,有合法格子走且还没到终点时,步数+1.
2.因为路障用2n-2个,所以要注意sx和sy要开的大一些。

2.3 AC代码

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

int tp[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int mp[1005][1005], sx[2010], sy[2010], step;
struct date {
	int x;
	int y;
};
queue <date> q;
date t;
int main() {
	//freopen("P3395_4.in", "r", stdin);
	//freopen("4.out", "w", stdout);
	int T, n;
	bool flag = 0;
	cin >> T;
	for (int i = 1; i <= T; i++) {
		flag = 0;
		cin >> n;
		if (n == 1) {
			cout << "Yes" << endl;
			continue;
		}
		for (int j = 1; j <= n; j++) {
			mp[j][0] = 1;
			mp[j][n + 1] = 1;
			mp[0][j] = 1;
			mp[n + 1][j] = 1;
			for (int k = 1; k <= n; k++) {
				mp[j][k] = 0;
			}
		}//初始化
		for (int j = 1; j <= 2 * n - 2; j++)
			cin >> sx[j] >> sy[j]; //障碍
		mp[1][1] = 1;
		t.x = 1;
		t.y = 1;
		step = 1;
		q.push(t);
		while (q.size() > 0) {
			date tt = q.front();

			q.pop();
			for (int j = 0; j < 4; j++) {
				int tx = tt.x + tp[j][1];
				int ty = tt.y + tp[j][0];
				date mt;
				mt.x = tx;
				mt.y = ty;
				if (tx == n && ty == n && mp[tx][ty] == 0) {
					flag = 1;
					break;
				}
				if (mp[tx][ty] == 0) {
					mp[tx][ty] = 1;
					q.push(mt);
				}

			}
			if (flag)
				break;
			if (step <= 2 * n - 2) {
				mp[sx[step]][sy[step]] = 1;
				step++;
			}
		}
		if (flag) {
			cout << "Yes" << endl;
		} else
			cout << "No" << endl;
		while (q.size() > 0)
			q.pop();
	}


	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]。 - **严格波动处理**:仅当相邻元素严格不等时更新状态,符合题意要求。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值