Skip to content

NOIP 380. 校门外的树

发布于2020-03-12 17:24

题干

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入

第一行有两个整数L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。

接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

text
500 3
150 300
100 200
470 471

输出

输出包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。

text
298

来源

NOIP 2005年普及组第二题,收录于NOIPOJ第380题

解题思路

题目第一句非常具有迷惑性,极其容易让人联想到数组长度就是L,实际上这里的长度指数轴上的长度,即0到L的距离,但在求解过程中,将0到L存入数组,总计存入L+1个数(数组长度)。

text
0到1的距离为1,0到1有2个数;
0到2的距离为2,0到2有3个数;
0到100的距离为100,0到100有101个数;
……
0到L的距离为L,0到L有L+1个数。

我的解法是将所有区间抽象为起点和终点,并叠加到一条线段上。

我将某一个起点记为+1,某一个终点记为-1,其余位置记为0,并使用一维数组trees来存储这条线段(即存储区间起点和终点的的叠加值)。

若两个起点位置相同,则叠加得到+2;若两个终点位置相同,则叠加得到-2;若某一个起点和某一个终点位置相同(两个区间合并为一个区间),则叠加得到0。

存储完所有区间后,遍历这条线段上的叠加结果,在遍历时将每个叠加结果依次累加到变量stack中。

因为起点和终点必然成对存在,即+1和-1的个数必定相等,那么当没有进入或已经离开所有区间的时候,stack必定为0,而只要遍历位置在任意一个区间时,累加的stack必定大于0(按我定义的正负来说)。

java
package top.qlin.leo;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sr = new Scanner(System.in);

		/** 马路上的树(没有移走之前),总共有L+1棵。 */
		byte[] trees = new byte[sr.nextInt() + 1];

		/** 区域个数。用于确定循环次数。 */
		int time = sr.nextInt();

		// 循环time次,读取所有“区域”。
		// 如果某区域的终点和另一个区域的起点重叠,这种方法可以自动合并两个区域。
		for (int t = 0; t < time; t++) {
			trees[sr.nextInt()]++;    // 这里输入的int是该区域的起点位置。
			trees[sr.nextInt()]--;    // 这里输入的int是区域的终点位置。
		}

		sr.close();

		/** 某一位置的“树”身处的区域的个数。 */
		int stack = 0,

		/** 当前未被移走的树的数量 */
			quantity = 0;

		// 遍历trees以统计剩余树的数量。
		for (int i = 0; i < trees.length; i++) {
			// stack != 0表示第i棵树身处stack个叠加的区域中。
			// trees[i] != 0表示第i棵树不在区域的两个端点上。
			if (trees[i] != 0 || stack != 0) {
				stack += trees[i];
			} else {
				quantity++;
			}
		}
		System.out.print(quantity);
	}
}