16.6 แบบฝึกหัด
เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น
ข้อ 1 สูงกว่าค่าเฉลี่ย
ครูจดคะแนนสอบย่อยของนักเรียนลงสมุดทีละคน จดเสร็จแล้วปิดท้ายด้วย -1 ครูไม่ได้นับไว้ว่ามีกี่คน ครูอยากรู้ว่ามีนักเรียนกี่คนที่ได้คะแนนสูงกว่าค่าเฉลี่ยของห้อง และคนเหล่านั้นได้คะแนนเท่าไรบ้าง
คนที่ได้คะแนนเท่ากับค่าเฉลี่ยพอดีไม่นับว่าสูงกว่า
ข้อมูลเข้า
คะแนนของนักเรียนทีละคนตามลำดับในสมุด คั่นด้วยช่องว่างหรือขึ้นบรรทัดใหม่ แต่ละคะแนนเป็นจำนวนเต็มตั้งแต่ 0 ถึง 100 และตามด้วย -1 ซึ่งไม่ใช่คะแนน มีนักเรียนอย่างน้อย 1 คน และไม่เกิน 100000 คน
ข้อมูลออก
บรรทัดแรกพิมพ์จำนวนนักเรียนที่ได้คะแนนสูงกว่าค่าเฉลี่ย
บรรทัดที่สองพิมพ์คะแนนของนักเรียนเหล่านั้นตามลำดับในสมุด คั่นด้วยช่องว่างหนึ่งช่อง ถ้าไม่มีนักเรียนที่สูงกว่าค่าเฉลี่ยเลย ไม่ต้องพิมพ์บรรทัดที่สอง
72 85 64 90 58 -12
85 9080 80 80
80 -10ดูคำใบ้
ต้องรู้ค่าเฉลี่ยก่อนถึงจะตัดสินได้ว่าใครสูงกว่า จึงต้องเก็บคะแนนทุกตัวไว้แล้ววนรอบที่สอง ไม่รู้ว่ามีกี่คน ใช้ vector แล้ว push_back ไปเรื่อยๆ จนเจอ -1 ส่วนการเทียบกับค่าเฉลี่ย ไม่ต้องใช้ทศนิยมเลย คะแนน x สูงกว่าค่าเฉลี่ยเมื่อ x × จำนวนคน > ผลรวม
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<int> score;
int x;
while (cin >> x && x != -1) {
score.push_back(x);
}
long long total = 0;
for (int s : score) {
total += s;
}
long long count = score.size();
vector<int> above;
for (int s : score) {
if (s * count > total) {
above.push_back(s);
}
}
cout << above.size() << '\n';
if (!above.empty()) {
for (int i = 0; i < (int)above.size(); i++) {
if (i > 0) {
cout << ' ';
}
cout << above[i];
}
cout << '\n';
}
return 0;
}ข้อ 2 หมุนรูป
แอปแต่งรูปเก็บรูปขาวดำเป็นตารางตัวเลข R แถว C คอลัมน์ แต่ละช่องคือความสว่างของจุดหนึ่งจุด ตั้งแต่ 0 (ดำ) ถึง 255 (ขาว) ให้หมุนรูปไปทางขวา 90 องศาตามเข็มนาฬิกา แถวบนสุดของรูปเดิมจะกลายเป็นคอลัมน์ขวาสุดของรูปใหม่ รูปใหม่จึงมี C แถว R คอลัมน์
ขนาดของรูปรู้ตอนรันเท่านั้น และรูปอาจผอมยาวมาก เช่น 1 แถว 200000 คอลัมน์ หรือ 200000 แถว 1 คอลัมน์
ข้อนี้ตั้งใจให้ฝึกย้ายวิธีจากข้อหมุนรูปภาพในบทที่ 7 มาเขียนด้วย C++ วิธีหมุนเหมือนเดิม ที่ต่างคือขนาดของรูป ซึ่งอาเรย์สองมิติขนาดตายตัวรับไม่ไหว ต้องใช้ vector ที่สร้างตามขนาดจริง
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม R และ C คั่นด้วยช่องว่าง (1 ≤ R, C และ R × C ≤ 200000)
อีก R บรรทัด บรรทัดละ C จำนวน คือความสว่างของแต่ละแถวจากบนลงล่าง แต่ละจำนวนเป็นจำนวนเต็มตั้งแต่ 0 ถึง 255
ข้อมูลออก
รูปที่หมุนแล้ว C บรรทัด บรรทัดละ R จำนวน คั่นด้วยช่องว่างหนึ่งช่อง
2 3
1 2 3
4 5 64 1
5 2
6 33 2
0 255
128 64
10 2010 128 0
20 64 255ดูคำใบ้
อาเรย์สองมิติขนาดตายตัวต้องจองไว้ 200000 × 200000 ช่องถึงจะรับได้ทั้งรูปผอมแนวนอนและแนวตั้ง ซึ่งใหญ่เกินไปมาก ใช้ vector<vector<int>> ที่สร้างตามขนาดจริงหลังอ่าน R กับ C แล้ว ลองหมุนตัวอย่างด้วยมือก่อน แถวที่ i ของรูปใหม่คือคอลัมน์ที่ i ของรูปเดิม อ่านจากล่างขึ้นบน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int rows, cols;
cin >> rows >> cols;
vector<vector<int>> photo(rows, vector<int>(cols));
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
cin >> photo[r][c];
}
}
for (int c = 0; c < cols; c++) {
for (int r = rows - 1; r >= 0; r--) {
cout << photo[r][c];
if (r > 0) {
cout << ' ';
}
}
cout << '\n';
}
return 0;
}ข้อ 3 ร้านใกล้บ้าน
แอปสั่งอาหารมีร้านในระบบ N ร้าน ร้านที่ i อยู่ห่างจากบ้านของน้อง di เมตร หน้าแรกของแอปจะแสดงร้านที่ใกล้ที่สุด K ร้าน เรียงจากใกล้ไปไกล ถ้าสองร้านอยู่ห่างเท่ากัน ร้านที่มีหมายเลขน้อยกว่าขึ้นก่อน ให้หาว่าหน้าแรกแสดงร้านหมายเลขอะไรบ้าง
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง (1 ≤ K ≤ N ≤ 100000)
บรรทัดที่สองมีระยะทาง d1 ถึง dN ของร้านหมายเลข 1 ถึง N คั่นด้วยช่องว่าง แต่ละค่าเป็นจำนวนเต็มตั้งแต่ 0 ถึง 109
ข้อมูลออก
บรรทัดเดียว หมายเลขร้าน K ร้านตามลำดับที่แสดง คั่นด้วยช่องว่างหนึ่งช่อง
5 3
450 120 800 120 3002 4 54 4
700 700 700 504 1 2 3ดูคำใบ้
เก็บแต่ละร้านเป็น pair<int, int> ของ (ระยะทาง, หมายเลขร้าน) โดยเอาระยะทางไว้ใน first เพราะ pair เทียบ first ก่อน ถ้าเท่ากันค่อยเทียบ second ซึ่งตรงกับกติกาของโจทย์พอดี เรียงด้วย sort ได้เลยโดยไม่ต้องเขียน lambda แล้วพิมพ์ second ของ K ตัวแรก ระวังหมายเลขร้านเริ่มจาก 1 ไม่ใช่ 0
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<pair<int, int>> shops(n);
for (int i = 0; i < n; i++) {
cin >> shops[i].first;
shops[i].second = i + 1;
}
sort(shops.begin(), shops.end());
for (int i = 0; i < k; i++) {
if (i > 0) {
cout << ' ';
}
cout << shops[i].second;
}
cout << '\n';
return 0;
}ข้อ 4 ซื้อ 3 จ่าย 2
ร้านเครื่องเขียนจัดโปรโมชัน "ซื้อ 3 จ่าย 2" ลูกค้าวางสินค้าบนสายพานเรียงเป็นแถว แคชเชียร์แบ่งสินค้าเป็นกลุ่มละสามชิ้นตามลำดับบนสายพาน คือชิ้นที่ 1 ถึง 3 ชิ้นที่ 4 ถึง 6 ไปเรื่อยๆ ในแต่ละกลุ่ม ชิ้นที่ถูกที่สุดได้ฟรี ถ้ามีหลายชิ้นถูกที่สุดเท่ากัน ฟรีแค่ชิ้นเดียว ส่วนสินค้าที่เหลือท้ายแถวไม่ครบสามชิ้นต้องจ่ายเต็มราคา
น้องซื้อสินค้า N ชิ้น และเลือกได้ว่าจะวางชิ้นไหนก่อนหลัง ให้หาว่าต้องจ่ายน้อยที่สุดกี่บาท
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 200000)
บรรทัดที่สองมีราคาของสินค้า N ชิ้น คั่นด้วยช่องว่าง แต่ละราคาเป็นจำนวนเต็มตั้งแต่ 1 ถึง 106 บาท
ข้อมูลออก
ราคารวมที่ต้องจ่ายน้อยที่สุด
6
120 35 80 50 15 602854
100 100 100 100300ดูคำใบ้
ชิ้นที่แพงที่สุดไม่มีทางได้ฟรี เพราะในกลุ่มของมันต้องมีชิ้นที่ถูกกว่าหรือเท่ากัน ทางที่ดีที่สุดคือให้สองชิ้นที่แพงที่สุดพาชิ้นที่แพงอันดับสามไปฟรีด้วย แล้วทำแบบเดิมกับที่เหลือ เรียงราคาจากมากไปน้อยด้วย sort กับ greater<int>() แล้วรวมทุกชิ้นยกเว้นชิ้นที่ index เป็น 2, 5, 8, ... ราคารวมถึง 2 × 1011 ต้องใช้ long long และ N ถึงสองแสน เรียงด้วยลูปซ้อนไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> price(n);
for (int &p : price) {
cin >> p;
}
sort(price.begin(), price.end(), greater<int>());
long long total = 0;
for (int i = 0; i < n; i++) {
if (i % 3 != 2) {
total += price[i];
}
}
cout << total << '\n';
return 0;
}ข้อ 5 รายชื่อหน้าห้อง
โรงเรียนเปิดรับสมัครค่ายวิทยาศาสตร์ นักเรียน N คนกรอกใบสมัครเรียงตามเวลาที่ส่ง แต่ละใบมีชื่อเล่น เลขห้อง และคะแนนสอบคัดเลือก ครูต้องการพิมพ์รายชื่อเพื่อติดหน้าห้องตามกติกานี้
- เรียงตามเลขห้องจากน้อยไปมาก
- ในห้องเดียวกัน คะแนนมากกว่าอยู่ก่อน
- ถ้าห้องเดียวกันและคะแนนเท่ากัน คนที่ส่งใบสมัครก่อนอยู่ก่อน
ชื่อเล่นซ้ำกันได้ เพราะนักเรียนต่างห้องอาจชื่อเดียวกัน หรือแม้แต่ในห้องเดียวกันก็มีได้
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 100000)
อีก N บรรทัด เรียงตามเวลาที่ส่งใบสมัคร บรรทัดละหนึ่งคน มีชื่อเล่น เลขห้อง และคะแนน คั่นด้วยช่องว่าง ชื่อเล่นเป็นตัวพิมพ์เล็กภาษาอังกฤษยาว 1 ถึง 10 ตัว เลขห้องเป็นจำนวนเต็มตั้งแต่ 1 ถึง 20 คะแนนเป็นจำนวนเต็มตั้งแต่ 0 ถึง 100
ข้อมูลออก
N บรรทัดตามลำดับในรายชื่อ บรรทัดละหนึ่งคน มีเลขห้อง ชื่อเล่น และคะแนน คั่นด้วยช่องว่างหนึ่งช่อง
6
ploy 3 85
beam 1 70
mint 3 92
film 1 70
earth 2 60
fah 3 851 beam 70
1 film 70
2 earth 60
3 mint 92
3 ploy 85
3 fah 854
nam 5 40
nam 5 40
palm 2 100
nam 2 1002 palm 100
2 nam 100
5 nam 40
5 nam 40ดูคำใบ้
เก็บแต่ละคนเป็น struct แล้วเขียน lambda ที่เทียบเลขห้องก่อน ถ้าห้องต่างกันห้องน้อยมาก่อน ถ้าห้องเท่ากันคะแนนมากมาก่อน เงื่อนไขที่สามคือลำดับที่ส่งใบสมัคร มีสองทาง ใช้ stable_sort ซึ่งรักษาลำดับเดิมของคนที่เทียบแล้วเท่ากันให้เอง หรือเก็บลำดับที่รับเข้ามาไว้ใน struct แล้วใช้เป็นเงื่อนไขสุดท้ายของ sort ถ้าใช้ sort เฉยๆ ตัวอย่างเล็กๆ อาจบังเอิญผ่าน แต่ชุดทดสอบที่มีคนคะแนนเท่ากันเยอะจะผิด
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
struct Student {
string name;
int room;
int score;
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<Student> list(n);
for (Student &s : list) {
cin >> s.name >> s.room >> s.score;
}
stable_sort(list.begin(), list.end(), [](const Student &a, const Student &b) {
if (a.room != b.room) {
return a.room < b.room;
}
return a.score > b.score;
});
for (const Student &s : list) {
cout << s.room << ' ' << s.name << ' ' << s.score << '\n';
}
return 0;
}ข้อ 6 รหัสกุญแจ
น้องลืมรหัสกุญแจตู้เก็บของ จำได้แค่ว่ารหัสใช้ตัวเลขชุดหนึ่ง ใช้ครบทุกตัวตัวละหนึ่งครั้ง แต่จำลำดับไม่ได้ และจำได้อีกอย่างว่ารหัสนั้นเป็นจำนวนที่หารด้วย K ลงตัว ตัวเลขในชุดอาจซ้ำกันได้ เช่น ชุด 1 1 2 สร้างรหัสที่ต่างกันได้แค่ 112 121 และ 211
รหัสที่มีหลายหลักขึ้นต้นด้วย 0 ไม่ได้ ถ้าชุดมีตัวเลขตัวเดียว รหัส 0 ใช้ได้
ให้หาว่ามีรหัสที่เป็นไปได้กี่แบบ และรหัสที่น้อยที่สุดคืออะไร
ข้อมูลเข้า
บรรทัดเดียว มีสตริง S ซึ่งเป็นตัวเลขในชุด เขียนติดกัน ยาว 1 ถึง 8 ตัว และจำนวนเต็ม K (1 ≤ K ≤ 106) คั่นด้วยช่องว่าง
ข้อมูลออก
บรรทัดแรกพิมพ์จำนวนรหัสที่ต่างกันทั้งหมดที่ใช้ตัวเลขครบชุดและหารด้วย K ลงตัว
บรรทัดที่สองพิมพ์รหัสที่น้อยที่สุดในบรรดารหัสเหล่านั้น ถ้าไม่มีเลยให้พิมพ์ -1
1203 318
1023112 71
112ดูคำใบ้
ตัวเลขไม่เกิน 8 ตัว ลองทุกลำดับได้ไม่เกิน 8! = 40320 แบบ ใช้ next_permutation กับสตริง S ได้เลย แต่ต้อง sort S ก่อน ไม่อย่างนั้นจะได้แค่ลำดับที่มากกว่าลำดับเริ่มต้น next_permutation ข้ามลำดับที่ซ้ำให้เอง จึงนับรหัสที่ต่างกันได้ตรงๆ ลำดับแรกที่ใช้ได้คือรหัสที่น้อยที่สุด เพราะรหัสทุกตัวยาวเท่ากัน อย่าลืมข้ามรหัสที่ขึ้นต้นด้วย 0 เมื่อ S ยาวกว่า 1 ตัว
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
string digits;
int k;
cin >> digits >> k;
sort(digits.begin(), digits.end());
int count = 0;
int smallest = -1;
do {
if (digits.size() > 1 && digits[0] == '0') {
continue;
}
int value = 0;
for (char ch : digits) {
value = value * 10 + (ch - '0');
}
if (value % k == 0) {
count++;
if (smallest == -1) {
smallest = value;
}
}
} while (next_permutation(digits.begin(), digits.end()));
cout << count << '\n';
cout << smallest << '\n';
return 0;
}ข้อ 7 สองไฟล์ลงแฟลชไดรฟ์
น้องมีไฟล์วิดีโอ N ไฟล์ ไฟล์ที่ i มีขนาด si ไบต์ และมีแฟลชไดรฟ์ที่ว่างอยู่ B ไบต์ น้องอยากเลือกไฟล์สองไฟล์ที่ต่างกันไปให้เพื่อน โดยต้องใส่ทั้งสองไฟล์ลงแฟลชไดรฟ์ได้ คือขนาดรวมไม่เกิน B ให้นับว่ามีวิธีเลือกกี่วิธี
การเลือกไฟล์ที่ i กับไฟล์ที่ j นับเป็นวิธีเดียวกับการเลือกไฟล์ที่ j กับไฟล์ที่ i และไฟล์สองไฟล์ที่ขนาดเท่ากันยังถือเป็นคนละไฟล์
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ B คั่นด้วยช่องว่าง (2 ≤ N ≤ 200000 และ 1 ≤ B ≤ 4 × 109)
บรรทัดที่สองมีขนาดของไฟล์ N ไฟล์ คั่นด้วยช่องว่าง แต่ละขนาดเป็นจำนวนเต็มตั้งแต่ 1 ถึง 2 × 109
ข้อมูลออก
จำนวนวิธีเลือกไฟล์สองไฟล์ที่ขนาดรวมไม่เกิน B
5 1500
700 300 1200 500 90064 8
4 4 4 46ดูคำใบ้
เทียบทุกคู่ต้องทำราว 2 × 1010 ครั้ง ไม่ทัน ให้เรียงขนาดจากน้อยไปมากก่อน แล้วสำหรับไฟล์ที่ i ไฟล์ที่จับคู่ได้คือไฟล์หลังจากมันที่ขนาดไม่เกิน B - si ซึ่งอยู่ติดกันเป็นช่วงในข้อมูลที่เรียงแล้ว นับได้ด้วย upper_bound ที่เริ่มค้นจาก v.begin() + i + 1 มีสามจุดที่ต้องใช้ long long คือ B ขนาดรวมของสองไฟล์ และคำตอบ ซึ่งอาจถึงราว 2 × 1010
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long space;
cin >> n >> space;
vector<int> size(n);
for (int &s : size) {
cin >> s;
}
sort(size.begin(), size.end());
long long ways = 0;
for (int i = 0; i < n; i++) {
long long left = space - size[i];
auto start = size.begin() + i + 1;
ways += upper_bound(start, size.end(), left) - start;
}
cout << ways << '\n';
return 0;
}ข้อ 8 บ้านที่ใกล้จุดพบลูกน้ำ
ถนนสายหนึ่งในหมู่บ้านเป็นเส้นตรงยาว มีบ้าน N หลังตั้งอยู่ริมถนน บ้านหลังที่ i อยู่ที่ตำแหน่ง xi เมตร นับจากหลักกิโลเมตรศูนย์ ค่าติดลบคืออยู่ก่อนถึงหลัก บ้านหลายหลังอาจอยู่ตำแหน่งเดียวกันได้ เช่น ห้องแถวที่อยู่ในตึกเดียวกัน
อาสาสมัครสาธารณสุขเดินสำรวจริมถนนแล้วพบแหล่งน้ำขังที่มีลูกน้ำยุงลาย Q จุด จุดที่ j อยู่ที่ตำแหน่ง pj ทุกจุดที่พบ อาสาสมัครจะไปแจ้งบ้านที่อยู่ใกล้จุดนั้นที่สุดให้ช่วยเทน้ำทิ้ง ระยะระหว่างตำแหน่ง a กับ b คือ |a - b| ถ้ามีบ้านสองตำแหน่งที่ห่างเท่ากัน ให้ไปแจ้งบ้านที่ตำแหน่งน้อยกว่า ให้หาว่าแต่ละจุดต้องไปแจ้งบ้านที่ตำแหน่งไหน
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง (1 ≤ N, Q ≤ 200000)
บรรทัดที่สองมีตำแหน่งของบ้าน N หลัง คั่นด้วยช่องว่าง แต่ละตำแหน่งเป็นจำนวนเต็มตั้งแต่ -109 ถึง 109 ไม่ได้เรียงมา
บรรทัดที่สามมีตำแหน่ง p1 ถึง pQ ของจุดที่พบลูกน้ำ คั่นด้วยช่องว่าง แต่ละตำแหน่งเป็นจำนวนเต็มตั้งแต่ -109 ถึง 109
ข้อมูลออก
Q บรรทัด บรรทัดที่ j คือตำแหน่งของบ้านที่ต้องไปแจ้งสำหรับจุดที่ j
6 6
120 -40 300 120 75 500
100 0 400 -500 900 120120
-40
300
-40
500
1203 4
10 30 30
20 25 35 1010
30
30
10ดูคำใบ้
ไล่ดูทุกบ้านในทุกครั้งต้องทำ N × Q ถึง 4 × 1010 ครั้ง ไม่ทัน เรียงตำแหน่งบ้านครั้งเดียว แล้วบ้านที่ใกล้ p ที่สุดมีแค่สองหลังที่เป็นไปได้ คือหลังแรกที่ตำแหน่งไม่น้อยกว่า p ซึ่งได้จาก lower_bound ตามหน้า 16.5 กับหลังที่อยู่ก่อนหน้ามันหนึ่งช่อง ให้เก็บเป็นเลขช่อง k แบบตัวอย่างในหน้านั้น ระวังสองกรณี ถ้า k เท่ากับ N แปลว่าทุกหลังอยู่ก่อน p ไม่มีหลังที่ k และถ้า k เป็น 0 ก็ไม่มีหลังก่อนหน้า อ่านช่องที่ไม่มีอยู่จริงไม่มี error เตือน อาจได้ค่าขยะหรือโปรแกรมพัง ถ้าสองหลังห่างเท่ากันให้เลือกหลังก่อนหน้า เพราะตำแหน่งน้อยกว่า อย่าลืมเปิด ios::sync_with_stdio(false) เพราะข้อมูลเข้ามีหกแสนตัวเลข
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<int> house(n);
for (int &x : house) {
cin >> x;
}
sort(house.begin(), house.end());
for (int i = 0; i < q; i++) {
int p;
cin >> p;
int k = lower_bound(house.begin(), house.end(), p) - house.begin();
int answer;
if (k == n) {
answer = house[n - 1];
} else if (k == 0) {
answer = house[0];
} else if (p - house[k - 1] <= house[k] - p) {
answer = house[k - 1];
} else {
answer = house[k];
}
cout << answer << '\n';
}
return 0;
}ข้อ 9 ห้องอ่านหนังสือ
ห้องอ่านหนังสือของห้องสมุดใช้บัตรนักเรียนแตะเข้าออก ระบบเก็บประวัติไว้ N รายการ รายการที่ i บอกว่านักเรียนคนหนึ่งแตะเข้าที่เวลา si และแตะออกที่เวลา ei เวลาเป็นวินาทีนับจากวันที่ระบบเริ่มบันทึก นักเรียนคนนั้นอยู่ในห้องตั้งแต่วินาทีที่ si จนถึงก่อนวินาทีที่ ei ถ้าคนหนึ่งแตะออกที่เวลา t และอีกคนแตะเข้าที่เวลา t พอดี ถือว่าสองคนนี้ไม่ได้อยู่ในห้องพร้อมกัน
บรรณารักษ์อยากรู้ว่าห้องเคยมีคนอยู่พร้อมกันมากที่สุดกี่คน และจำนวนนั้นเกิดขึ้นครั้งแรกที่เวลาเท่าไร
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 200000)
อีก N บรรทัด บรรทัดละหนึ่งรายการ มีจำนวนเต็ม s และ e คั่นด้วยช่องว่าง (0 ≤ s < e ≤ 109) รายการไม่ได้เรียงตามเวลา
ข้อมูลออก
บรรทัดเดียว มีจำนวนคนที่อยู่พร้อมกันมากที่สุด ตามด้วยช่องว่าง แล้วตามด้วยเวลาแรกสุดที่ห้องมีคนจำนวนนั้น
5
10 50
20 30
40 70
25 45
60 803 253
0 10
10 20
20 301 0ดูคำใบ้
แยกแต่ละรายการเป็นสองเหตุการณ์ คือ (s, +1) ตอนมีคนเข้า และ (e, -1) ตอนมีคนออก เก็บเป็น pair<int, int> ใน vector เดียวกัน แล้ว sort ตามเวลา จากนั้นไล่เหตุการณ์ทีละตัว บวก second เข้าไปในตัวนับจำนวนคนในห้อง ค่ามากสุดของตัวนับคือคำตอบแรก ส่วนเวลาที่ห้องมีคนออกกับมีคนเข้าพร้อมกัน ต้องให้คนออกก่อน ไม่อย่างนั้นจะนับเกิน ลองคิดดูว่า pair เรียง (t, -1) กับ (t, +1) แบบไหน เวลาแรกที่ตัวนับขึ้นไปถึงค่ามากสุดคือคำตอบที่สอง ถ้าเทียบทุกคู่จะใช้ราว 2 × 1010 ครั้ง ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<pair<int, int>> events;
events.reserve(2 * n);
for (int i = 0; i < n; i++) {
int enter, leave;
cin >> enter >> leave;
events.push_back({enter, 1});
events.push_back({leave, -1});
}
sort(events.begin(), events.end());
int inside = 0;
int best = 0;
int best_time = 0;
for (auto [time, change] : events) {
inside += change;
if (inside > best) {
best = inside;
best_time = time;
}
}
cout << best << ' ' << best_time << '\n';
return 0;
}ข้อ 10 อันดับไม่ข้ามเลข
เกมมือถือเกมหนึ่งจัดอันดับผู้เล่น N คนจากแต้มสะสม แต้มมากได้อันดับดีกว่า ผู้เล่นที่แต้มเท่ากันได้อันดับเดียวกัน และอันดับถัดไปไม่ข้ามเลข เช่น แต้ม 90 85 85 70 ได้อันดับ 1 2 2 3 ไม่ใช่ 1 2 2 4 อย่างการจัดอันดับแบบนับจำนวนคนที่ดีกว่า
ให้บอกอันดับของผู้เล่นทุกคนตามลำดับในข้อมูลเข้า
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 200000)
บรรทัดที่สองมีแต้มของผู้เล่นคนที่ 1 ถึงคนที่ N คั่นด้วยช่องว่าง แต่ละค่าเป็นจำนวนเต็มตั้งแต่ 0 ถึง 109
ข้อมูลออก
บรรทัดเดียว อันดับของผู้เล่นคนที่ 1 ถึงคนที่ N คั่นด้วยช่องว่างหนึ่งช่อง
4
85 90 70 852 1 3 27
300 1200 300 950 1200 0 9503 1 3 2 1 4 2ดูคำใบ้
อันดับของผู้เล่นคือ 1 บวกจำนวนแต้มที่ไม่ซ้ำกันที่มากกว่าแต้มของเขา สร้างรายการแต้มที่ไม่ซ้ำโดยคัดลอก vector ไปอีกตัว sort แล้ว erase คู่กับ unique จากนั้นแต่ละคนหาตำแหน่งแต้มของตัวเองในรายการนั้นด้วย lower_bound ถ้ารายการเรียงจากน้อยไปมากและมี D ค่า แต้มที่ตำแหน่ง p มีค่าที่มากกว่าอยู่ D - 1 - p ค่า ถ้าไล่นับทุกคู่จะใช้ราว 4 × 1010 ครั้ง ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> points(n);
for (int &p : points) {
cin >> p;
}
vector<int> distinct = points;
sort(distinct.begin(), distinct.end());
distinct.erase(unique(distinct.begin(), distinct.end()), distinct.end());
int d = distinct.size();
for (int i = 0; i < n; i++) {
int pos = lower_bound(distinct.begin(), distinct.end(), points[i]) - distinct.begin();
if (i > 0) {
cout << ' ';
}
cout << d - pos;
}
cout << '\n';
return 0;
}