23.6 แบบฝึกหัด
เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น
ข้อ 1 ตู้แลกเหรียญที่ท่าเรือ
เกาะแห่งหนึ่งใช้เหรียญ K ชนิด ทุกชนิดมีไม่จำกัด เหรียญ 1 บาทมีเสมอ และเหรียญที่ใหญ่กว่าทุกชนิดมีค่าเป็นจำนวนเท่าของเหรียญที่เล็กกว่าทุกชนิด เช่น 1 2 10 50 ซึ่ง 10 เป็นจำนวนเท่าของ 2 และ 50 เป็นจำนวนเท่าของ 10 ตู้แลกเหรียญที่ท่าเรือต้องจ่ายเงิน Q ครั้ง ครั้งละ A บาทพอดี ให้หาว่าแต่ละครั้งต้องจ่ายเหรียญน้อยที่สุดกี่เหรียญ
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม K และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ K ≤ 30 และ 1 ≤ Q ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม K ตัว คือค่าของเหรียญแต่ละชนิด ไม่ซ้ำกัน และไม่ได้เรียงมา ทุกค่าไม่เกิน 109 มีเหรียญ 1 อยู่ด้วยเสมอ และสำหรับเหรียญสองชนิดใดๆ ชนิดที่ใหญ่กว่าหารด้วยชนิดที่เล็กกว่าลงตัว
อีก Q บรรทัด บรรทัดละหนึ่งจำนวน คือ A โดยที่ 1 ≤ A ≤ 1018
ข้อมูลออก
Q บรรทัด บรรทัดที่ j คือจำนวนเหรียญที่น้อยที่สุดของการจ่ายครั้งที่ j
4 3
10 1 50 2
38
87
1007
8
21 2
1
5
15
1ดูคำใบ้
เมื่อเหรียญใหญ่เป็นจำนวนเท่าของเหรียญเล็กทุกชนิด เหรียญเล็กชนิดหนึ่งจะมีได้ไม่ถึงจำนวนที่รวมกันเท่าเหรียญถัดไป ไม่งั้นแลกเป็นเหรียญที่ใหญ่กว่าได้ เหตุผลแบบเดียวกับเหรียญ 1 2 5 10 ในหน้า 23.1 จึงใช้ greedy ได้ เหรียญมาไม่เรียง ต้องเรียงจากมากไปน้อยก่อน แล้วใช้การหารแทนการลบทีละเหรียญ เพราะ A ถึง 1018 ถ้ามีแต่เหรียญ 1 บาท ต้องลบถึง 1018 ครั้ง และคำตอบก็เกิน int ด้วย
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int k, q;
cin >> k >> q;
vector<long long> coins(k);
for (int i = 0; i < k; i++) {
cin >> coins[i];
}
sort(coins.rbegin(), coins.rend());
for (int j = 0; j < q; j++) {
long long amount;
cin >> amount;
long long total = 0;
for (int i = 0; i < k; i++) {
total += amount / coins[i];
amount %= coins[i];
}
cout << total << '\n';
}
return 0;
}ข้อ 2 ตะลุยเทศกาลหนัง
เทศกาลภาพยนตร์มีหนัง N เรื่อง ฉายคนละโรง เรื่องที่ i ฉายตั้งแต่นาทีที่ si ถึงนาทีที่ ei น้องอยากดูให้ได้มากเรื่องที่สุด ทุกเรื่องที่ดูต้องดูตั้งแต่ต้นจนจบ ดูได้ทีละเรื่อง และโรงหนังอยู่ห่างกัน หลังหนังเรื่องหนึ่งจบที่นาทีที่ e ต้องเดินอีก G นาที เรื่องถัดไปที่จะดูจึงต้องเริ่มที่นาทีที่ e + G หรือหลังจากนั้น ถ้า G เป็น 0 เรื่องถัดไปเริ่มตอนเรื่องก่อนจบพอดีได้ เรื่องแรกที่ดูจะเริ่มเมื่อไรก็ได้
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ G คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 0 ≤ G ≤ 109
อีก N บรรทัด บรรทัดที่ i มีจำนวนเต็ม si และ ei โดยที่ 0 ≤ si < ei ≤ 109 หนังสองเรื่องอาจมีเวลาฉายเหมือนกันทุกอย่างได้
ข้อมูลออก
จำนวนเรื่องที่มากที่สุดที่ดูได้
6 10
0 100
90 200
110 180
190 300
200 260
270 40044 0
60 120
0 60
30 90
120 1803ดูคำใบ้
ถ้าบวกเวลาเดิน G เข้าไปท้ายทุกเรื่อง โจทย์จะกลายเป็นการเลือกกิจกรรมที่ไม่ซ้อนกันในหน้า 23.2 พอดี เรียงตามเวลาจบ แล้วรับเรื่องที่เริ่มไม่ก่อนเวลาจบของเรื่องล่าสุดบวก G กฎเริ่มเร็วสุดก่อนหรือสั้นสุดก่อนผิด ตัวอย่างในหน้า 23.2 ใช้ได้กับข้อนี้ด้วย วิธีไล่หาเรื่องที่จบเร็วที่สุดที่ยังดูทันทีละรอบเป็น O(N2) ไม่ทันเมื่อ N เป็นสองแสน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long gap;
cin >> n >> gap;
vector<pair<long long, long long>> films(n);
for (int i = 0; i < n; i++) {
long long start, finish;
cin >> start >> finish;
films[i] = {finish, start};
}
sort(films.begin(), films.end());
int count = 0;
long long ready = 0;
for (int i = 0; i < n; i++) {
long long finish = films[i].first;
long long start = films[i].second;
if (start >= ready) {
count++;
ready = finish + gap;
}
}
cout << count << '\n';
return 0;
}ข้อ 3 กล่องผลไม้อบแห้ง
ร้านผลไม้อบแห้งมีผลไม้ N ชนิด ชนิดที่ i เหลืออยู่ wi กรัม ถ้าขายหมดทั้งหมดได้ vi บาท ร้านตักขายเป็นกรัม ตักชนิดไหนไปเท่าไรก็ได้ไม่เกินที่มี และคิดราคาตามสัดส่วน เช่นตักไปครึ่งหนึ่งของที่มี ก็ได้ราคาครึ่งหนึ่งของ vi น้องจะจัดกล่องของขวัญที่ใส่ได้ไม่เกิน W กรัม และอยากให้ของในกล่องมีราคารวมสูงที่สุด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ W คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100000 และ 1 ≤ W ≤ 1011
อีก N บรรทัด บรรทัดที่ i มีจำนวนเต็ม wi และ vi โดยที่ 1 ≤ wi, vi ≤ 106
ข้อมูลออก
ราคารวมที่สูงที่สุดเป็นบาท ทศนิยม 2 ตำแหน่ง ชุดทดสอบเลือกไว้แล้วว่าคำตอบจริงไม่อยู่ใกล้กึ่งกลางระหว่างสองค่าที่ปัดได้
4 50
20 300
30 360
25 400
10 100760.002 7
3 10
5 1219.60ดูคำใบ้
ตักแบ่งได้ จึงไม่ต้องเลือกว่าจะเอาทั้งชนิดหรือไม่เอาเลย สิ่งที่ต้องดูคือราคาต่อกรัม ไม่ใช่ราคารวมหรือน้ำหนัก เรียงจากราคาต่อกรัมมากไปน้อยด้วยการคูณไขว้ v[a] * w[b] > v[b] * w[a] ห้ามหารจำนวนเต็มเพราะเศษหาย ใส่ทั้งชนิดจนกว่าใส่ไม่ได้ แล้วตักชนิดถัดไปเท่าที่เหลือที่ว่าง ถ้า W มากกว่าน้ำหนักรวมของทุกชนิด ก็ใส่ได้ทั้งหมด ราคารวมถึง 1011 บาท ต้องเก็บส่วนที่เป็นจำนวนเต็มด้วย long long
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long capacity;
cin >> n >> capacity;
vector<pair<long long, long long>> fruit(n);
for (int i = 0; i < n; i++) {
cin >> fruit[i].first >> fruit[i].second;
}
sort(fruit.begin(), fruit.end(), [](const pair<long long, long long> &a, const pair<long long, long long> &b) {
return a.second * b.first > b.second * a.first;
});
long long room = capacity, whole = 0;
double part = 0;
for (int i = 0; i < n && room > 0; i++) {
long long grams = fruit[i].first, price = fruit[i].second;
if (grams <= room) {
whole += price;
room -= grams;
} else {
part = (double)price * room / grams;
room = 0;
}
}
cout << fixed << setprecision(2) << whole + part << '\n';
return 0;
}ข้อ 4 เรือยางสองที่นั่ง
ค่ายลูกเสือพาน้อง N คนไปล่องแก่ง เรือยางหนึ่งลำนั่งได้ไม่เกินสองคน และรับน้ำหนักรวมได้ไม่เกิน L กิโลกรัม คนที่ i หนัก wi กิโลกรัม ทุกคนหนักไม่เกิน L จึงนั่งคนเดียวได้เสมอ ให้หาว่าต้องใช้เรืออย่างน้อยกี่ลำจึงพาทุกคนลงน้ำได้
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ L คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 1 ≤ L ≤ 109
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ w1 ถึง wN โดยที่ 1 ≤ wi ≤ L
ข้อมูลออก
จำนวนเรือที่น้อยที่สุด
5 100
70 50 30 80 2035 10
5 5 5 5 53ดูคำใบ้
คนที่หนักที่สุดต้องลงเรือสักลำแน่ๆ คู่ที่ดีที่สุดของเขาคือคนที่เบาที่สุด เพราะถ้าคนเบาที่สุดยังนั่งด้วยไม่ได้ ก็ไม่มีใครนั่งด้วยได้ และถ้านั่งด้วยได้ การเอาคนเบาที่สุดไปแลกกับคู่ของเขาในคำตอบอื่นก็ไม่ทำให้ใช้เรือเพิ่ม เรียงน้ำหนัก แล้วใช้สองตัวชี้ที่เดินเข้าหากัน ต่างจากสองตัวชี้ในหน้า 18.5 ที่เดินไปทางเดียวกัน ตัวชี้ light เริ่มที่คนเบาสุด heavy เริ่มที่คนหนักสุด ทุกรอบคน heavy ได้เรือหนึ่งลำแล้วเลื่อน heavy ไปทางซ้าย ถ้าคน light นั่งด้วยได้ก็ให้ลงลำเดียวกันแล้วเลื่อน light ไปทางขวา ทำจน light เลย heavy ระวังตอน light เท่ากับ heavy ซึ่งเหลือคนเดียว ห้ามนับเขาเป็นคู่กับตัวเอง ตัวชี้ทั้งสองเดินรวมกันไม่เกิน N ก้าว จึงเป็น O(N) หลังเรียง การจับคู่คนที่น้ำหนักติดกันหลังเรียงผิด ลองกับ L = 10 และน้ำหนัก 1 2 8 9
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long limit;
cin >> n >> limit;
vector<long long> weight(n);
for (int i = 0; i < n; i++) {
cin >> weight[i];
}
sort(weight.begin(), weight.end());
int light = 0, heavy = n - 1, boats = 0;
while (light <= heavy) {
if (light < heavy && weight[light] + weight[heavy] <= limit) {
light++;
}
heavy--;
boats++;
}
cout << boats << '\n';
return 0;
}ข้อ 5 นัดติวช่วงปิดเทอม
ช่วงปิดเทอม ครูจะนัดนักเรียน N คนมาติวสอบเข้า นักเรียนคนที่ i ว่างตั้งแต่วันที่ ai ถึงวันที่ bi รวมทั้งสองวันนั้น วันไหนที่ครูนัด นักเรียนทุกคนที่ว่างวันนั้นจะมา ครูอยากให้นักเรียนทุกคนได้มาติวอย่างน้อยหนึ่งครั้ง โดยนัดให้น้อยวันที่สุด ให้หาว่าต้องนัดอย่างน้อยกี่วัน
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
อีก N บรรทัด บรรทัดที่ i มีจำนวนเต็ม ai และ bi โดยที่ 1 ≤ ai ≤ bi ≤ 109
ข้อมูลออก
จำนวนวันที่น้อยที่สุดที่ต้องนัด
5
1 4
2 6
5 8
7 10
9 933
3 3
3 3
3 31ดูคำใบ้
ดูนักเรียนคนที่วันว่างวันสุดท้ายมาถึงเร็วที่สุด ต้องมีวันนัดสักวันในช่วงของเขา และนัดวันสุดท้ายที่เขาว่างดีที่สุด เพราะทุกคนที่ว่างวันที่เร็วกว่านั้นในช่วงของเขา ก็ยังว่างวันสุดท้ายนี้ด้วย คนที่ช่วงยังไม่จบจึงมีโอกาสถูกครอบคลุมมากที่สุด เรียงตาม bi แล้วไล่ไป คนไหนที่ ai อยู่หลังวันนัดล่าสุด ให้นัดเพิ่มที่ bi ของเขา คนที่ว่างวันแรกตรงกับวันนัดล่าสุดพอดีมาได้แล้ว ไม่ต้องนัดเพิ่ม
ดูเฉลย
#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>> students(n);
for (int i = 0; i < n; i++) {
int first_day, last_day;
cin >> first_day >> last_day;
students[i] = {last_day, first_day};
}
sort(students.begin(), students.end());
int sessions = 0, last_session = 0;
for (int i = 0; i < n; i++) {
if (students[i].second > last_session) {
sessions++;
last_session = students[i].first;
}
}
cout << sessions << '\n';
return 0;
}ข้อ 6 ร้านถ่ายรูปติดบัตร
ร้านถ่ายรูปติดบัตรหน้าโรงเรียนมีช่างภาพ K คน วันเปิดเทอมมีนักเรียน N คนมารอพร้อมกันตั้งแต่ร้านเปิด คนที่ i ใช้เวลาถ่าย ti นาที ช่างภาพแต่ละคนถ่ายได้ทีละคน ถ่ายเสร็จแล้วรับคนต่อไปได้ทันที เจ้าของร้านจัดได้เองว่าใครถ่ายกับช่างคนไหน และลำดับไหน เวลารอของนักเรียนคนหนึ่งคือเวลาตั้งแต่ร้านเปิดจนเริ่มถ่ายของเขา ให้หาผลรวมของเวลารอของทุกคนที่น้อยที่สุด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ K ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ t1 ถึง tN โดยที่ 1 ≤ ti ≤ 106
ข้อมูลออก
ผลรวมของเวลารอที่น้อยที่สุดเป็นนาที
5 2
5 2 8 3 4114 1
7 3 1 514ดูคำใบ้
ถ้ามีช่างคนเดียว คือโจทย์คิวในหน้า 23.3 ให้คนที่ใช้เวลาน้อยที่สุดก่อน เมื่อมีช่าง K คน ลองดูว่าเวลาถ่ายของแต่ละคนถูกนับซ้ำกี่ครั้ง คนที่ถ่ายกับช่างคนหนึ่ง ทำให้ทุกคนที่ต่อคิวหลังเขากับช่างคนเดียวกันต้องรอเพิ่ม ถ้าเรียงเวลาจากมากไปน้อย คน K คนแรกควรเป็นคนสุดท้ายของช่างแต่ละคน ไม่มีใครรอเพราะเขา K คนถัดไปถูกนับคนละหนึ่งครั้ง และต่อไปเรื่อยๆ คนลำดับที่ j (เริ่มนับ 0) จึงถูกนับ j / K ครั้ง ผลรวมถึงราว 1016 ใช้ long long วิธีไล่หาคนที่ใช้เวลาน้อยสุดทีละคนเป็น O(N2) ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<long long> minutes(n);
for (int i = 0; i < n; i++) {
cin >> minutes[i];
}
sort(minutes.begin(), minutes.end(), greater<long long>());
long long total = 0;
for (int j = 0; j < n; j++) {
total += minutes[j] * (j / k);
}
cout << total << '\n';
return 0;
}ข้อ 7 รวมกองข้าวเปลือก
โรงสีข้าวรับซื้อข้าวเปลือกจากชาวนา N ราย กองไว้ N กองบนลาน กองที่ i หนัก wi กิโลกรัม ก่อนสีต้องรวมทุกกองให้เป็นกองเดียว รถตักรวมได้ครั้งละสองกอง การรวมกองหนัก x กับกองหนัก y เสียค่าน้ำมัน x + y บาท ได้กองใหม่หนัก x + y ซึ่งเอาไปรวมกับกองอื่นต่อได้ ให้หาค่าน้ำมันรวมที่น้อยที่สุด ถ้ามีกองเดียวตั้งแต่แรก ค่าน้ำมันเป็น 0
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ w1 ถึง wN โดยที่ 1 ≤ wi ≤ 109
ข้อมูลออก
ค่าน้ำมันรวมที่น้อยที่สุดเป็นบาท
4
40 10 30 201901
5000ดูคำใบ้
กองที่ถูกรวมก่อนจะถูกนับซ้ำในค่าน้ำมันทุกครั้งที่รวมต่อ จึงควรรวมสองกองที่เบาที่สุดที่มีอยู่ตอนนั้นทุกครั้ง กองใหม่อาจไม่ใช่กองที่เบาที่สุดในรอบถัดไป เรียงครั้งเดียวแล้วรวมเรียงแถวจึงผิด ใช้ priority_queue แบบน้อยสุดก่อน น้ำหนักรวมถึง 2 × 1014 และค่าน้ำมันรวมมากกว่านั้นอีก ต้องใช้ long long ทั้งใน heap และตัวนับ วิธีไล่หาสองกองที่เบาที่สุดใหม่ทุกรอบเป็น O(N2) ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
priority_queue<long long, vector<long long>, greater<long long>> piles;
for (int i = 0; i < n; i++) {
long long weight;
cin >> weight;
piles.push(weight);
}
long long cost = 0;
while (piles.size() > 1) {
long long a = piles.top();
piles.pop();
long long b = piles.top();
piles.pop();
cost += a + b;
piles.push(a + b);
}
cout << cost << '\n';
return 0;
}ข้อ 8 จัดลำดับทำรายงาน
ใกล้สอบปลายภาค น้องมีรายงานค้างอยู่ N ชิ้น ชิ้นที่ i ต้องใช้เวลาทำ ti วันเต็ม และครูกำหนดส่งภายในสิ้นวันที่ di น้องเริ่มทำได้ตั้งแต่วันที่ 1 ทำทีละชิ้นจนเสร็จแล้วค่อยเริ่มชิ้นต่อไป แต่มีวันสอบ K วันที่ต้องไปสอบทั้งวัน ทำรายงานไม่ได้เลย ชิ้นที่ทำค้างอยู่ต้องหยุดไว้ แล้วทำต่อในวันถัดไปที่ไม่ใช่วันสอบ วันอื่นนอกจากวันสอบทำรายงานได้ทุกวัน ถ้าชิ้นใดทำเสร็จสิ้นวันที่ f ซึ่งเลยกำหนดส่ง ชิ้นนั้นสาย f - di วัน ชิ้นที่เสร็จทันถือว่าสาย 0 วัน ครูหักคะแนนตามชิ้นที่สายมากที่สุด น้องจึงอยากเลือกลำดับการทำให้ชิ้นที่สายมากที่สุดสายน้อยที่สุด ให้หาว่าสายน้อยที่สุดได้กี่วัน ถ้าส่งทันทุกชิ้นได้ให้ตอบ 0
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 0 ≤ K ≤ 200000
อีก N บรรทัด บรรทัดที่ i มีจำนวนเต็ม ti และ di โดยที่ 1 ≤ ti ≤ 109 และ 1 ≤ di ≤ 1014
ถ้า K มากกว่า 0 บรรทัดสุดท้ายมีจำนวนเต็ม K ตัว คือวันสอบ ไม่ซ้ำกันและไม่ได้เรียงมา แต่ละตัวอยู่ระหว่าง 1 ถึง 1014 ถ้า K เป็น 0 จะไม่มีบรรทัดนี้
ข้อมูลออก
จำนวนวันที่สายน้อยที่สุดของชิ้นที่สายมากที่สุด
5 2
3 4
2 2
1 7
4 9
2 5
6 353 0
2 10
3 5
1 10ดูคำใบ้
วันสอบไม่ได้เปลี่ยนว่าควรทำชิ้นไหนก่อน ชิ้นใดจะเสร็จวันไหนขึ้นกับว่าทำงานสะสมไปแล้วกี่วันเท่านั้น และยิ่งทำสะสมมากก็ยิ่งเสร็จช้า เหตุผลการสลับสองชิ้นที่ติดกันจึงยังใช้ได้ ลำดับที่ดีที่สุดคือเรียงตามวันกำหนดส่งจากเร็วไปช้า ทำชิ้นที่สั้นที่สุดก่อนผิด ลองกับชิ้น 1 วันส่งวันที่ 100 และชิ้น 5 วันส่งวันที่ 5 เรียงตามเวลาที่เหลือ di - ti ก็ผิด ลองกับชิ้น 1 วันส่งวันที่ 2 และชิ้น 10 วันส่งวันที่ 10 ส่วนที่ต้องคิดเพิ่มคือแปลงจำนวนวันทำงานสะสม W เป็นวันในปฏิทิน เรียงวันสอบก่อน วันที่เสร็จคือ W บวกจำนวนวันสอบที่ไม่เกินวันนั้น เพราะ W เพิ่มขึ้นเรื่อยๆ จึงใช้ตัวชี้ที่เดินไปข้างหน้าอย่างเดียวในรายการวันสอบได้ ไม่ต้องไล่ทีละวัน วันที่ทำเสร็จได้ถึงราว 2 × 1014 ใช้ long long การลองทุกลำดับมี N! แบบ ใช้ได้แค่ N ไม่เกินราว 10
ดูเฉลย
#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<long long, long long>> tasks(n);
for (int i = 0; i < n; i++) {
long long days, due;
cin >> days >> due;
tasks[i] = {due, days};
}
vector<long long> exams(k);
for (int i = 0; i < k; i++) {
cin >> exams[i];
}
sort(tasks.begin(), tasks.end());
sort(exams.begin(), exams.end());
long long worked = 0, worst = 0;
int passed = 0;
for (int i = 0; i < n; i++) {
worked += tasks[i].second;
while (passed < k && exams[passed] <= worked + passed) {
passed++;
}
long long finish = worked + passed;
worst = max(worst, finish - tasks[i].first);
}
cout << worst << '\n';
return 0;
}ข้อ 9 งานพิเศษช่วงปิดเทอม
ช่วงปิดเทอม น้องกับเพื่อนรับจ้างทำงานพิเศษเป็นทีม มีผู้จ้าง N รายเสนองานมา ทุกงานใช้เวลาหนึ่งวันพอดี ทีมทำได้วันละไม่เกิน C งาน เริ่มทำได้ตั้งแต่วันที่ 1 งานที่ i ต้องทำในวันที่ di หรือก่อนหน้านั้น และได้ค่าจ้าง pi บาท งานที่ไม่รับไม่ต้องทำ ให้หาค่าจ้างรวมที่มากที่สุดที่ทีมได้
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ C คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 1 ≤ C ≤ 109
อีก N บรรทัด บรรทัดที่ i มีจำนวนเต็ม di และ pi โดยที่ 1 ≤ di ≤ 109 และ 1 ≤ pi ≤ 109
ข้อมูลออก
ค่าจ้างรวมที่มากที่สุดเป็นบาท
6 2
1 300
1 500
1 200
2 400
2 100
3 5013505 1
2 500
1 300
2 400
3 200
1 6001300ดูคำใบ้
มองจากวันกำหนดที่เร็วที่สุดก่อน งานที่วันกำหนดไม่เกิน d ทำได้รวมไม่เกิน C × d งาน ถ้ารับไว้เกินจำนวนนั้น ต้องทิ้งงานที่ค่าจ้างน้อยที่สุดในกลุ่มที่รับไว้ ซึ่ง heap แบบน้อยสุดก่อนหาให้ได้ทันที C × d ใหญ่ถึง 1018 คูณใน int แล้วล้น ต้องใช้ long long di ใหญ่ถึง 109 จึงสร้างอาเรย์ของวันไม่ได้ (ถ้าจะลงงานในวันที่ยังว่างวันสุดท้าย ให้สังเกตว่ามีงานไม่เกิน N งาน วันกำหนดที่เกิน N จึงเท่ากับ N) ลงวันแรกที่ว่างผิด และเรียงตามวันกำหนดแล้วรับเมื่อยังมีที่โดยไม่คืนงานก็ผิด ค่าจ้างรวมเกิน int
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long per_day;
cin >> n >> per_day;
vector<pair<long long, long long>> jobs(n);
for (int i = 0; i < n; i++) {
cin >> jobs[i].first >> jobs[i].second;
}
sort(jobs.begin(), jobs.end());
priority_queue<long long, vector<long long>, greater<long long>> kept;
long long total = 0;
for (int i = 0; i < n; i++) {
kept.push(jobs[i].second);
total += jobs[i].second;
if ((long long)kept.size() > per_day * jobs[i].first) {
total -= kept.top();
kept.pop();
}
}
cout << total << '\n';
return 0;
}ข้อ 10 ลบเลขให้เหลือน้อยที่สุด
เกมในคาบคณิตศาสตร์ ครูเขียนตัวเลข N หลักบนกระดาน แล้วให้นักเรียนลบหลักออกให้ครบ K หลักพอดี หลักที่เหลือเลื่อนมาชิดกันโดยลำดับไม่เปลี่ยน ใครได้ตัวเลขที่เหลือน้อยที่สุดเป็นผู้ชนะ เช่น 1432219 ลบ 3 หลัก ลบ 4 3 และ 2 ตัวแรกออก เหลือ 1219 ซึ่งน้อยที่สุดแล้ว ถ้าเหลือเลข 0 นำหน้า ให้ถือว่าเป็นตัวเลขตามปกติ เช่น 0200 คือ 200 ให้หาตัวเลขที่น้อยที่สุดที่เป็นไปได้
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 0 ≤ K < N
บรรทัดที่สองเป็นตัวเลข N หลักติดกัน ไม่ขึ้นต้นด้วย 0
ข้อมูลออก
ตัวเลขที่น้อยที่สุดที่เหลือ ไม่มีเลข 0 นำหน้า ถ้าเหลือแต่เลข 0 ให้พิมพ์ 0
7 3
143221912195 1
10200200ดูคำใบ้
ตัวเลขที่จำนวนหลักเท่ากัน ตัวที่หลักซ้ายสุดน้อยกว่าคือตัวที่น้อยกว่า จึงควรทำให้หลักซ้ายๆ เล็กที่สุดก่อน ไล่หลักจากซ้ายไปขวา เก็บหลักที่เลือกไว้ใน stack แบบหน้า 17.3 (ใช้ string ที่ push_back กับ pop_back ได้) ถ้าหลักใหม่น้อยกว่าหลักบนสุดของ stack และยังลบได้อีก ให้ลบหลักบนสุดทิ้ง ทำซ้ำจนลบไม่ได้แล้วค่อยใส่หลักใหม่ ไล่ครบแล้วถ้ายังลบไม่ครบ K หลัก ให้ลบจากท้าย เพราะหลักที่เหลือเรียงจากน้อยไปมากแล้ว สุดท้ายตัดเลข 0 นำหน้า ลบหลักที่มากที่สุด K หลักผิด ลองกับ 1432219 ส่วนการวนหาจุดที่หลักถัดไปน้อยกว่าแล้วลบทีละหลักเป็น O(NK) ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
string digits;
cin >> n >> k >> digits;
string kept;
for (char c : digits) {
while (k > 0 && !kept.empty() && kept.back() > c) {
kept.pop_back();
k--;
}
kept.push_back(c);
}
while (k > 0) {
kept.pop_back();
k--;
}
int start = 0;
while (start + 1 < (int)kept.size() && kept[start] == '0') {
start++;
}
cout << kept.substr(start) << '\n';
return 0;
}