題目
某校大門外長度為L的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1米。我們可以把馬路看成一個數軸,馬路的一端在數軸0的位置,另一端在L的位置;數軸上的每個整數點,即0,1,2,……,L,都種有一棵樹。
由於馬路上有一些區域要用來建地鐵。這些區域用它們在數軸上的起始點和終止點表示。已知任一區域的起始點和終止點的坐標都是整數,區域之間可能有重合的部分。現在要把這些區域中的樹(包括區域端點處的兩棵樹)移走。你的任務是計算將這些樹都移走后,馬路上還有多少棵樹。
輸入
第一行有兩個整數L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表馬路的長度,M代表區域的數目,L和M之間用一個空格隔開。
接下來的M行每行包含兩個不同的整數,用一個空格隔開,表示一個區域的起始點和終止點的坐標。
500 3
150 300
100 200
470 471
輸出
輸出包括一行,這一行只包含一個整數,表示馬路上剩余的樹的數目。
298
來源
NOIP 2005年普及組第二題,收錄於NOIPOJ第380題。
解題思路
題目第一句非常具有迷惑性,極其容易讓人聯想到數組長度就是L,實際上這里的長度指數軸上的長度,即0到L的距離,但在求解過程中,將0到L存入數組,總計存入L+1個數(數組長度)。
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(按我定義的正負來說)。
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);
}
}