24.6 แบบฝึกหัด
เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น
ข้อ 1 กบกระโดดข้ามหิน
ริมบึงมีก้อนหินวางเรียงเป็นแถวตรง เรียกว่าก้อนที่ 1 2 3 ไปเรื่อยๆ กบแต่ละตัวเริ่มอยู่บนฝั่ง ซึ่งนับเป็นตำแหน่ง 0 แต่ละครั้งกบกระโดดไปข้างหน้าได้ 1 2 หรือ 3 ก้อน และไม่เคยกระโดดถอยหลัง สองวิธีถือว่าต่างกันถ้าลำดับระยะที่กระโดดต่างกัน เช่น 1 แล้ว 3 กับ 3 แล้ว 1 เป็นคนละวิธี
คืนนี้มีกบ Q ตัว ตัวที่ j อยากไปหยุดที่ก้อนที่ Nj พอดี ให้ตอบทีละตัวว่ากบตัวนั้นมีวิธีกระโดดกี่วิธี คำตอบอาจใหญ่มาก ให้ตอบเป็นเศษจากการหารด้วย 1000000007
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม Q โดยที่ 1 ≤ Q ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม Q ตัว คือ N1 ถึง NQ โดยที่ 1 ≤ Nj ≤ 1000000 อาจซ้ำกันและไม่ได้เรียงมา
ข้อมูลออก
Q บรรทัด บรรทัดที่ j คือจำนวนวิธีของกบตัวที่ j หารด้วย 1000000007 เหลือเศษเท่าไร
3
4 6 17
24
12
3 34
4ดูคำใบ้
ให้ ways[i] คือจำนวนวิธีไปหยุดที่ก้อน i ครั้งสุดท้ายกบกระโดดมาจากก้อน i - 1 i - 2 หรือ i - 3 จึงบวกสามช่องนั้น กรณีฐานคือ ways[0] = 1 และระวังอย่าอ่านช่องติดลบ สามค่ารวมกันเกิน int ได้ ให้ mod ทุกครั้งที่บวก ส่วนที่ต้องคิดเพิ่มคือคำถามมีถึงแสนข้อ แต่ทุกข้อใช้ตารางเดียวกัน ถ้าเติมตารางใหม่ทุกคำถาม งานรวมคือ Q × N ถึง 1011 ไม่ทัน ให้เติมตารางครั้งเดียวถึงก้อนที่ไกลที่สุดที่ถูกถาม แล้วตอบทุกคำถามด้วยการเปิดตาราง อ่านและพิมพ์เป็นแสนบรรทัด ใช้ ios::sync_with_stdio(false) กับ '\n'
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> q;
vector<int> stones(q);
int farthest = 0;
for (int j = 0; j < q; j++) {
cin >> stones[j];
farthest = max(farthest, stones[j]);
}
vector<int> ways(farthest + 1, 0);
ways[0] = 1;
for (int i = 1; i <= farthest; i++) {
long long total = 0;
for (int jump = 1; jump <= 3 && jump <= i; jump++) {
total += ways[i - jump];
}
ways[i] = total % MOD;
}
for (int j = 0; j < q; j++) {
cout << ways[stones[j]] << '\n';
}
return 0;
}ข้อ 2 ขึ้นบันไดให้เหนื่อยน้อยที่สุด
บันไดขึ้นจุดชมวิวมี N ขั้น เรียกว่าขั้นที่ 1 ถึง N แต่ละขั้นสูงไม่เท่ากัน ขั้นที่ i ทำให้เหนื่อย ci หน่วยเมื่อเหยียบ น้องเริ่มยืนที่พื้น ซึ่งไม่ทำให้เหนื่อย แต่ละก้าวขึ้นได้ 1 หรือ 2 ขั้น และต้องไปหยุดที่ขั้นที่ N ซึ่งต้องเหยียบด้วย ความเหนื่อยรวมคือผลรวมของ ci ทุกขั้นที่เหยียบ ให้หาความเหนื่อยรวมที่น้อยที่สุด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ c1 ถึง cN โดยที่ 0 ≤ ci ≤ 109
ข้อมูลออก
ความเหนื่อยรวมที่น้อยที่สุด
6
3 8 2 7 1 9155
1 5 100 5 111ดูคำใบ้
ให้ tired[i] คือความเหนื่อยรวมน้อยที่สุดที่ไปหยุดที่ขั้น i ได้ ก้าวสุดท้ายมาจากขั้น i - 1 หรือ i - 2 จึงได้ c[i] + min(tired[i - 1], tired[i - 2]) กรณีฐานคือพื้นซึ่งไม่เหนื่อยเลย การเลือกก้าวไปขั้นที่เหนื่อยน้อยกว่าทีละก้าวแบบ greedy ผิดได้ ลองกับตัวอย่างที่สอง และผลรวมถึง 2 × 1014 ต้องใช้ long long
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> cost(n + 1);
for (int i = 1; i <= n; i++) {
cin >> cost[i];
}
vector<long long> tired(n + 1);
tired[0] = 0;
tired[1] = cost[1];
for (int i = 2; i <= n; i++) {
tired[i] = cost[i] + min(tired[i - 1], tired[i - 2]);
}
cout << tired[n] << '\n';
return 0;
}ข้อ 3 ตุ้มน้ำหนักน้อยที่สุด
ห้องแล็บวิทยาศาสตร์มีตุ้มน้ำหนักสำหรับตาชั่งสองแขน N แบบ แบบที่ i หนัก wi กรัม แต่ละแบบมีมากพอที่จะใช้กี่ลูกก็ได้ ครูต้องการวางตุ้มบนจานข้างเดียวให้หนักรวมพอดี X กรัม โดยใช้ตุ้มน้อยลูกที่สุด ให้หาว่าต้องใช้กี่ลูก ถ้าวางให้หนักพอดี X กรัมไม่ได้เลย ให้ตอบ -1
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ X คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100 และ 1 ≤ X ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ w1 ถึง wN ไม่ซ้ำกัน โดยที่ 1 ≤ wi ≤ 100000
ข้อมูลออก
จำนวนตุ้มที่น้อยที่สุด หรือ -1 ถ้าทำไม่ได้
3 6
1 3 422 23
5 7-14 11
9 6 5 12ดูคำใบ้
ให้ fewest[x] คือจำนวนตุ้มน้อยที่สุดที่รวมได้ x กรัม ตุ้มลูกสุดท้ายหนักเท่าไรก็ได้ที่ไม่เกิน x กรณีฐานคือ fewest[0] = 0 ช่องอื่นเริ่มที่ INF ที่บวกหนึ่งแล้วไม่ล้น เช่น 109 ถ้าจบแล้ว fewest[X] ยังเป็น INF ให้พิมพ์ -1 หยิบตุ้มหนักสุดก่อนแบบ greedy ผิดกับตัวอย่างที่สาม งานทั้งหมดคือ X × N ไม่เกิน 107
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int INF = 1000000000;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, target;
cin >> n >> target;
vector<int> weights(n);
for (int i = 0; i < n; i++) {
cin >> weights[i];
}
vector<int> fewest(target + 1, INF);
fewest[0] = 0;
for (int x = 1; x <= target; x++) {
for (int w : weights) {
if (w <= x) {
fewest[x] = min(fewest[x], fewest[x - w] + 1);
}
}
}
if (fewest[target] == INF) {
cout << -1 << '\n';
} else {
cout << fewest[target] << '\n';
}
return 0;
}ข้อ 4 สวนมะม่วงกับรังผึ้ง
สวนมะม่วงของโรงเรียนปลูกต้นมะม่วงเรียงเป็นแถวเดียว N ต้น ต้นที่ i มีผลสุก ai ลูก นักเรียนจะเขย่าต้นมะม่วงเพื่อเก็บผล เขย่าต้นไหนก็ได้ผลสุกของต้นนั้นทั้งหมด แต่ต้นที่ปลูกติดกันมีรังผึ้งเกาะกิ่งที่พันกันอยู่ ถ้าเขย่าสองต้นที่ติดกัน ผึ้งจะแตกรัง จึงห้ามเขย่าต้นที่ i กับต้นที่ i + 1 ทั้งคู่ ให้หาว่าเก็บมะม่วงได้มากที่สุดกี่ลูก
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ a1 ถึง aN โดยที่ 0 ≤ ai ≤ 109
ข้อมูลออก
จำนวนมะม่วงที่เก็บได้มากที่สุด
7
20 50 30 10 60 40 301403
50 60 50100ดูคำใบ้
ให้ best[i] คือมะม่วงมากที่สุดจากต้นที่ 1 ถึง i ต้นที่ i เก็บหรือไม่เก็บ ไม่เก็บได้ best[i - 1] เก็บได้ best[i - 2] + a[i] เลือกต้นที่มีผลมากที่สุดก่อนแบบ greedy ผิดกับตัวอย่างที่สอง ผลรวมถึง 2 × 1014 ต้องใช้ long long
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> fruits(n + 1);
for (int i = 1; i <= n; i++) {
cin >> fruits[i];
}
vector<long long> best(n + 1);
best[0] = 0;
best[1] = fruits[1];
for (int i = 2; i <= n; i++) {
best[i] = max(best[i - 1], best[i - 2] + fruits[i]);
}
cout << best[n] << '\n';
return 0;
}ข้อ 5 ช่วงที่ยอดขายดีที่สุด
ร้านเครื่องเขียนหน้าโรงเรียนจดกำไรรายวันไว้ N วัน วันที่ i ได้กำไร ai บาท วันไหนขาดทุนจะจดเป็นค่าติดลบ เจ้าของร้านอยากรู้ว่าช่วงวันติดกันช่วงไหนที่กำไรรวมมากที่สุด ช่วงต้องมีอย่างน้อยหนึ่งวัน
ถ้ามีหลายช่วงที่กำไรรวมเท่ากัน ให้ตอบช่วงที่เริ่มเร็วที่สุด ถ้ายังเริ่มวันเดียวกัน ให้ตอบช่วงที่จบเร็วที่สุด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ a1 ถึง aN โดยที่ -109 ≤ ai ≤ 109
ข้อมูลออก
บรรทัดแรกคือกำไรรวมที่มากที่สุด บรรทัดที่สองคือวันเริ่มและวันจบของช่วงนั้น คั่นด้วยช่องว่าง
8
-3 5 -2 4 -6 3 2 -17
2 44
-5 -2 -8 -3-2
2 2ดูคำใบ้
ใช้ Kadane ในหน้า 24.4 คู่กับการจดวันเริ่มในหน้า 24.5 ให้ ending[i] คือผลรวมมากสุดของช่วงที่จบวันที่ i พอดี ถ้า ending[i - 1] ไม่ติดลบให้ต่อจากช่วงเดิม (ต่อเมื่อเป็น 0 ด้วย เพื่อให้เริ่มเร็วที่สุด) ไม่งั้นเริ่มใหม่ จดวันเริ่มไว้ทุกช่อง แล้วเปลี่ยนคำตอบเมื่อได้ผลรวมมากกว่า หรือได้เท่ากันแต่เริ่มเร็วกว่า ลองทุกคู่วันเริ่มวันจบเป็น 2 × 1010 คู่ ไม่ทัน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> profit(n + 1);
for (int i = 1; i <= n; i++) {
cin >> profit[i];
}
vector<long long> ending(n + 1);
vector<int> start(n + 1);
ending[1] = profit[1];
start[1] = 1;
int best_start = 1, best_end = 1;
for (int i = 2; i <= n; i++) {
if (ending[i - 1] >= 0) {
ending[i] = ending[i - 1] + profit[i];
start[i] = start[i - 1];
} else {
ending[i] = profit[i];
start[i] = i;
}
long long best = ending[best_end];
if (ending[i] > best || (ending[i] == best && start[i] < best_start)) {
best_start = start[i];
best_end = i;
}
}
cout << ending[best_end] << '\n';
cout << best_start << ' ' << best_end << '\n';
return 0;
}ข้อ 6 คุกกี้กล่อง
ร้านเบเกอรี่ขายคุกกี้เป็นกล่อง มีกล่อง N ขนาด กล่องขนาดที่ i บรรจุคุกกี้ si ชิ้น ร้านไม่แบ่งขายเป็นชิ้น ห้องเรียนต้องการคุกกี้พอดี X ชิ้นสำหรับงานเลี้ยง จะซื้อกล่องแต่ละขนาดกี่กล่องก็ได้ รวมถึงไม่ซื้อเลย ถามว่ามีวิธีซื้อกี่วิธี สองวิธีถือว่าต่างกันเมื่อจำนวนกล่องของขนาดใดขนาดหนึ่งไม่เท่ากัน ลำดับที่หยิบกล่องไม่สำคัญ
คำตอบอาจใหญ่มาก ให้ตอบเป็นเศษจากการหารด้วย 1000000007
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ X คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100 และ 1 ≤ X ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ s1 ถึง sN ไม่ซ้ำกัน โดยที่ 1 ≤ si ≤ 100000
ข้อมูลออก
จำนวนวิธีซื้อ หารด้วย 1000000007 เหลือเศษเท่าไร ถ้าซื้อให้พอดีไม่ได้เลย ให้ตอบ 0
3 5
1 2 542 10
3 41ดูคำใบ้
ข้อนี้นับแบบลำดับไม่สำคัญ ซื้อ 2 กล่องเล็กแล้ว 1 กล่องใหญ่ กับซื้อกล่องใหญ่ก่อน เป็นวิธีเดียวกัน ใช้ลูปนอกเป็นขนาดกล่อง ลูปในเป็นจำนวนชิ้นจากน้อยไปมาก ตามหน้า 24.3 ถ้าสลับลูปจะได้ 9 แทน 4 ในตัวอย่างแรก อย่าลืม mod ทุกครั้งที่บวก
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, target;
cin >> n >> target;
vector<int> sizes(n);
for (int i = 0; i < n; i++) {
cin >> sizes[i];
}
vector<int> ways(target + 1, 0);
ways[0] = 1;
for (int s : sizes) {
for (int x = s; x <= target; x++) {
ways[x] = (ways[x] + ways[x - s]) % MOD;
}
}
cout << ways[target] << '\n';
return 0;
}ข้อ 7 ถอดรหัสตัวเลข
ชมรมคอมพิวเตอร์ส่งข้อความลับกันด้วยรหัสง่ายๆ แทนตัวอักษร A ด้วย 1 B ด้วย 2 ไปจนถึง Z ด้วย 26 แล้วเขียนตัวเลขต่อกันโดยไม่เว้นวรรค เช่น BZ เขียนเป็น 226 ปัญหาคือ 226 อ่านกลับได้หลายแบบ คือ 2 2 6 (BBF) 22 6 (VF) และ 2 26 (BZ) ให้หาว่าตัวเลขที่ได้รับถอดกลับเป็นข้อความได้กี่แบบ
แต่ละตัวอักษรใช้ตัวเลขหนึ่งหรือสองหลักต่อกันเท่านั้น และต้องเป็นค่า 1 ถึง 26 ที่ไม่มีเลข 0 นำหน้า เช่น 06 ไม่ใช่ตัว F และ 0 ตัวเดียวไม่ใช่ตัวอักษรใด ถ้าถอดไม่ได้เลยให้ตอบ 0 คำตอบอาจใหญ่มาก ให้ตอบเป็นเศษจากการหารด้วย 1000000007
ข้อมูลเข้า
บรรทัดเดียว เป็นสตริงของตัวเลข 0 ถึง 9 ยาว L ตัว โดยที่ 1 ≤ L ≤ 1000000
ข้อมูลออก
จำนวนแบบที่ถอดได้ หารด้วย 1000000007 เหลือเศษเท่าไร
226310201ดูคำใบ้
ให้ ways[i] คือจำนวนวิธีถอดรหัสตัวเลข i ตัวแรก ตัวอักษรสุดท้ายมาจากตัวเลขหนึ่งหลัก (ตัวที่ i ต้องไม่ใช่ 0) หรือสองหลัก (ตัวที่ i - 1 กับ i ต้องเป็น 10 ถึง 26) จึงบวก ways[i - 1] กับ ways[i - 2] เฉพาะกรณีที่ใช้ได้ กรณีฐาน ways[0] = 1 ระวังเลข 0 เช่น 06 ไม่ใช่ตัว F และ 30 ถอดไม่ได้เลย
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string code;
cin >> code;
int n = code.size();
vector<int> ways(n + 1, 0);
ways[0] = 1;
for (int i = 1; i <= n; i++) {
if (code[i - 1] != '0') {
ways[i] = ways[i - 1];
}
if (i >= 2) {
int two = (code[i - 2] - '0') * 10 + (code[i - 1] - '0');
if (two >= 10 && two <= 26) {
ways[i] = (ways[i] + ways[i - 2]) % MOD;
}
}
}
cout << ways[n] << '\n';
return 0;
}ข้อ 8 ใบเสร็จแสตมป์
ร้านไปรษณีย์มีแสตมป์ N แบบ แบบที่ i ราคา pi บาท แต่ละแบบมีกี่ดวงก็ได้ ลูกค้าต้องติดแสตมป์ให้ราคารวมพอดี X บาท ใช้น้อยดวงที่สุด และพนักงานต้องพิมพ์ใบเสร็จบอกว่าใช้แสตมป์ราคาอะไรบ้าง
ใบเสร็จเรียงราคาจากมากไปน้อย ถ้ามีหลายชุดที่ใช้จำนวนดวงน้อยที่สุดเท่ากัน ให้เทียบใบเสร็จของแต่ละชุดทีละตำแหน่งจากซ้าย แล้วเลือกชุดที่ตำแหน่งแรกที่ต่างกันมีราคามากกว่า เช่น ราคา 4 บาทด้วยแสตมป์ 1 2 3 บาท ใช้ 2 ดวงได้ทั้ง 3 1 และ 2 2 ต้องตอบ 3 1
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ X คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100 และ 1 ≤ X ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ p1 ถึง pN ไม่ซ้ำกันและไม่ได้เรียงมา โดยที่ 1 ≤ pi ≤ 100000
ข้อมูลออก
ถ้าติดให้ราคาพอดี X บาทไม่ได้ ให้พิมพ์ -1 บรรทัดเดียว
ถ้าได้ บรรทัดแรกคือจำนวนดวงที่น้อยที่สุด K บรรทัดที่สองคือราคาของแสตมป์ K ดวง เรียงจากมากไปน้อย คั่นด้วยช่องว่าง
3 13
3 1 44
4 4 4 13 4
2 1 32
3 12 23
7 5-1ดูคำใบ้
เติม fewest[x] แบบข้อตุ้มน้ำหนักก่อน แล้วเดินย้อนจาก X ตามหน้า 24.5 ที่ x บาท ลองแสตมป์จากราคาแพงไปถูก เลือกดวงแรกที่ fewest[x - p] เท่ากับ fewest[x] - 1 แบบนี้ได้ดวงแพงที่สุดเท่าที่ยังใช้จำนวนดวงน้อยสุดได้ และดวงที่ได้ทีหลังไม่มีทางแพงกว่าดวงก่อน รายการจึงเรียงจากมากไปน้อยเองและตรงกับกติกาในโจทย์ ถ้าเก็บรายการทั้งชุดไว้ทุกช่องจะช้าและเปลืองหน่วยความจำเกินไป
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int INF = 1000000000;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, target;
cin >> n >> target;
vector<int> prices(n);
for (int i = 0; i < n; i++) {
cin >> prices[i];
}
sort(prices.rbegin(), prices.rend());
vector<int> fewest(target + 1, INF);
fewest[0] = 0;
for (int x = 1; x <= target; x++) {
for (int p : prices) {
if (p <= x) {
fewest[x] = min(fewest[x], fewest[x - p] + 1);
}
}
}
if (fewest[target] == INF) {
cout << -1 << '\n';
return 0;
}
cout << fewest[target] << '\n';
int x = target;
bool first = true;
while (x > 0) {
for (int p : prices) {
if (p <= x && fewest[x - p] == fewest[x] - 1) {
cout << (first ? "" : " ") << p;
first = false;
x -= p;
break;
}
}
}
cout << '\n';
return 0;
}ข้อ 9 กระโดดห้ามซ้ำระยะเดิม
ทีมกรีฑาซ้อมกระโดดขึ้นบันไดอัฒจันทร์ที่มี N ขั้น นักกีฬาเริ่มยืนที่พื้น ซึ่งนับเป็นขั้น 0 แต่ละครั้งกระโดดขึ้นได้ 1 2 หรือ 3 ขั้น และต้องไปหยุดที่ขั้น N พอดี โค้ชตั้งกติกาว่า ห้ามกระโดดระยะเดิมสองครั้งติดกัน เช่น กระโดด 2 ขั้นแล้วต้องตามด้วย 1 หรือ 3 ขั้นเท่านั้น ถามว่ามีวิธีกระโดดกี่วิธี สองวิธีถือว่าต่างกันถ้าลำดับระยะที่กระโดดต่างกัน
เช่น N = 4 มี 3 วิธี คือ 1 3, 3 1 และ 1 2 1 ส่วน 2 2 กับ 1 1 2 ผิดกติกา คำตอบอาจใหญ่มาก ให้ตอบเป็นเศษจากการหารด้วย 1000000007
ข้อมูลเข้า
บรรทัดเดียว มีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 1000000
ข้อมูลออก
จำนวนวิธี หารด้วย 1000000007 เหลือเศษเท่าไร
4379ดูคำใบ้
รู้แค่ว่าอยู่ขั้นไหนยังไม่พอ ต้องรู้ด้วยว่าก้าวล่าสุดยาวเท่าไร จึงจะรู้ว่าก้าวต่อไปห้ามยาวเท่าไร ให้สถานะเป็นสองค่า ways[i][j] คือจำนวนวิธีไปหยุดที่ขั้น i โดยก้าวสุดท้ายยาว j ขั้น ก้าวก่อนหน้าต้องไม่ยาว j จึงบวก ways[i - j][k] ทุก k ที่ไม่เท่ากับ j ส่วนก้าวแรกจากพื้นไม่มีก้าวก่อนหน้า ให้นับเป็นกรณีฐาน สถานะมี 3N ตัว งานต่อสถานะไม่เกิน 3
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main()
{
int n;
cin >> n;
// ways[j][i] คือจำนวนวิธีไปหยุดที่ขั้น i โดยก้าวสุดท้ายยาว j ขั้น
vector<vector<long long>> ways(4, vector<long long>(n + 1, 0));
for (int i = 1; i <= n; i++) {
for (int jump = 1; jump <= 3 && jump <= i; jump++) {
int from = i - jump;
long long total = 0;
if (from == 0) {
total = 1;
} else {
for (int last = 1; last <= 3; last++) {
if (last != jump) {
total += ways[last][from];
}
}
}
ways[jump][i] = total % MOD;
}
}
cout << (ways[1][n] + ways[2][n] + ways[3][n]) % MOD << '\n';
return 0;
}ข้อ 10 ตัดวันแย่ทิ้งได้หนึ่งวัน
น้องจดคะแนนความพร้อมของตัวเองก่อนสอบแข่งขันทุกวัน N วัน วันที่ i ได้ ai คะแนน วันที่ป่วยหรือนอนดึกคะแนนติดลบ ครูให้เลือกช่วงวันติดกันหนึ่งช่วงมาเขียนรายงาน คะแนนของช่วงคือผลรวมของทุกวันในช่วง และครูยอมให้ตัดวันใดวันหนึ่งในช่วงทิ้งได้ไม่เกินหนึ่งวัน แต่หลังตัดแล้วต้องเหลืออย่างน้อยหนึ่งวัน ให้หาคะแนนของช่วงที่มากที่สุดที่เป็นไปได้
เช่น คะแนน 6 -9 5 -1 4 เลือกทั้งห้าวันแล้วตัดวันที่ 2 ได้ 6 + 5 - 1 + 4 = 14 ซึ่งดีกว่าทุกช่วงที่ไม่ตัด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ a1 ถึง aN โดยที่ -109 ≤ ai ≤ 109
ข้อมูลออก
คะแนนของช่วงที่มากที่สุด
5
6 -9 5 -1 4143
-4 -2 -7-2ดูคำใบ้
เพิ่มข้อมูลลงในสถานะว่าตัดไปแล้วหรือยัง keep[i] คือผลรวมมากสุดของช่วงที่จบวันที่ i โดยยังไม่ตัด ซึ่งก็คือ Kadane ในหน้า 24.4 ส่วน cut[i] คือผลรวมมากสุดของช่วงที่จบวันที่ i และตัดไปแล้วหนึ่งวัน มาได้สองทาง คือตัดวันที่ i เอง ได้ keep[i - 1] หรือตัดวันก่อนหน้าไปแล้ว ได้ cut[i - 1] + a[i] คำตอบคือค่ามากสุดของทั้งสองตาราง ระวัง cut[1] ซึ่งตัดแล้วไม่เหลือวันไหนเลย
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
const long long NEG_INF = LLONG_MIN / 2;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> score(n + 1);
for (int i = 1; i <= n; i++) {
cin >> score[i];
}
vector<long long> keep(n + 1), cut(n + 1);
keep[1] = score[1];
cut[1] = NEG_INF;
long long answer = keep[1];
for (int i = 2; i <= n; i++) {
keep[i] = max(score[i], keep[i - 1] + score[i]);
cut[i] = max(keep[i - 1], cut[i - 1] + score[i]);
answer = max(answer, max(keep[i], cut[i]));
}
cout << answer << '\n';
return 0;
}