แผนการเรียน
/

การทบทวนการเขียนโปรแกรมด้วย C++
พื้นฐานสำคัญสู่การแข่งขันระดับสากล

วันที่ 1 ช่วงเช้า: ทบทวนตัวแปร, ชนิดข้อมูล และฟังก์ชันสำหรับการแข่งขัน

หัวข้อหลักในเช้านี้

1
ความสำคัญของภาษา C++ และโครงสร้าง Code Template
2
การจัดการตัวเลขและปัญหา Integer Overflow
3
การใช้งานฟังก์ชันและการส่งค่าแบบ Reference

ทำไมต้องเป็นภาษา C++?

ประสิทธิภาพความเร็ว

C++ ทำงานได้เร็วมากเมื่อเทียบกับ Python หรือ Java ซึ่งสำคัญมากในโจทย์ที่มีข้อจำกัดเวลา

Standard Library (STL)

มีไลบรารีที่รวบรวมโครงสร้างข้อมูลและอัลกอริทึมที่มีประสิทธิภาพไว้พร้อมใช้งาน

สถิติจาก Google Code Jam พบว่าผู้เข้าแข่งขันระดับท็อป 79% เลือกใช้ C++

เป้าหมาย: ถูกต้องและรวดเร็ว

  • โค้ดควรเขียนได้รวดเร็วและกระชับ (Straightforward & Concise)
  • ไม่จำเป็นต้องออกแบบซอฟต์แวร์ที่ดูแลรักษาระยะยาวเหมือนงานวิศวกรรม
  • เน้นการแก้ปัญหา (Problem Solving) และการใช้งานที่ถูกต้อง

C++ Code Template

#include <bits/stdc++.h> // รวมทุกไลบรารีมาตรฐาน

using namespace std; // เรียกใช้ std โดยตรง


int main() {

ios::sync_with_stdio(0); cin.tie(0); // Fast I/O

// โค้ดสำหรับแก้ปัญหาเขียนตรงนี้

return 0;

}

g++ Command

g++ -std=c++14 -O2 -Wall test.cpp -o test
-std=c++14: ใช้มาตรฐาน C++14 ที่รองรับในเกือบทุกสนาม
-O2: สั่งให้ Compiler ปรับแต่งโค้ดให้ทำงานได้เร็วที่สุด
-Wall: แสดงคำเตือน (Warnings) เกี่ยวกับข้อผิดพลาดที่อาจเกิดขึ้น

พื้นฐาน cin และ cout

int a, b; string x;

cin >> a >> b >> x; // อ่านข้อมูลแยกด้วยช่องว่างหรือบรรทัดใหม่

cout << a << " " << b << " " << x << "\n"; // แสดงผลข้อมูล

หมายเหตุ: การใช้ "\n" ทำงานได้เร็วกว่า endl เนื่องจากไม่ต้องสั่งล้างข้อมูลในบัฟเฟอร์ (flush) ทุกครั้ง

ชนิดข้อมูลที่ใช้ในการแข่งขัน

TypeDescriptionUsage
intจำนวนเต็ม 32 บิตใช้สำหรับจำนวนไม่เกิน 2 พันล้าน
long longจำนวนเต็ม 64 บิตห้ามลืมใช้เมื่อตัวเลขอาจเกิน 2 พันล้าน!
doubleทศนิยม 64 บิตการคำนวณที่ต้องการความแม่นยำสูง
boolค่าจริงหรือเท็จใช้ในการตรวจสอบเงื่อนไข (true/false)

Integer Overflow

ข้อผิดพลาดที่พบบ่อยที่สุด: การเก็บค่าเกินขีดจำกัดของตัวแปร

int a = 123456789;

long long b = a * a; // ผลลัพธ์ผิดพลาด!

long long b = (long long)a * a; // วิธีแก้ไขที่ถูกต้อง

ช่วงของ long long: ถึงประมาณ 9 x 1018 ซึ่งเพียงพอสำหรับโจทย์ส่วนใหญ่

Floating Point Accuracy

ทศนิยมในคอมพิวเตอร์ไม่สามารถเก็บค่าที่แท้จริงได้เสมอไป

double x = 0.3 * 3 + 0.1;
printf("%.20f\n", x); // 0.99999999999999988898

กฎเหล็ก: อย่าใช้ == กับ double!

ให้ตรวจสอบว่าผลต่างน้อยกว่าค่าความผิดพลาด (ε) หรือไม่

if (abs(a - b) < 1e-9) { /* เท่ากัน */ }

Shortening Code (รวดเร็วคือหัวใจ)

Type Definitions

typedef long long ll;

typedef vector<int> vi;

ช่วยให้ประกาศตัวแปรขนาดใหญ่ได้ในไม่กี่ตัวอักษร

Macros

#define F first

#define PB push_back

ระวัง: การใช้ Macro อาจทำให้เกิดข้อผิดพลาดในการ Debug ได้ง่าย

Modular Arithmetic (%)

โจทย์มักจะให้หาคำตอบ "Modulo 109 + 7" เพื่อป้องกันตัวเลขล้น

(a + b) % m = (a % m + b % m) % m

(a * b) % m = (a % m * b % m) % m

คำเตือน: ใน C++ ค่าลบ Modulo จะได้ค่าลบเสมอ
วิธีแก้: x = (x % m + m) % m;

Boolean Logic

ชนิดข้อมูล bool เก็บค่าได้เพียง true (1) หรือ false (0)

Operators:

! (NOT) — กลับค่า

&& (AND) — และ

|| (OR) — หรือ

Comparison:

== (เท่ากับ), != (ไม่เท่า)

<, > (น้อยกว่า, มากกว่า)

<=, >= (น้อยกว่า/มากกว่าเท่ากับ)

If, Else if, Else

if (condition) {

// ทำงานถ้าเป็นจริง

} else if (other_condition) {

// ทำงานถ้าเงื่อนไขแรกไม่จริงแต่เงื่อนไขนี้จริง

} else {

// ทำงานในกรณีอื่นๆ ทั้งหมด

}

ทิป: การใช้ else if ช่วยให้คอมพิวเตอร์ไม่ต้องตรวจสอบเงื่อนไขที่ซ้ำซ้อนกัน

For Loops

ใช้เมื่อทราบจำนวนรอบการทำงานที่แน่นอน

for (int i = 0; i < n; i++) {

// ส่วนของ code ที่ต้องการทำซ้ำ

}

Initialization:
กำหนดตัวแปรเริ่ม (i = 0)
Condition:
เช็คก่อนเริ่มรอบ (i < n)
Update:
ทำเมื่อจบรอบ (i++)

While Loops

ใช้เมื่อต้องการวนลูปจนกว่าเงื่อนไขจะเป็นเท็จ โดยไม่ระบุรอบแน่นอน

while (condition) {

// ทำงานตราบเท่าที่เงื่อนไขเป็นจริง

}

ในกรณีอ่านข้อมูลจนจบไฟล์ (End of File) สามารถใช้ while (cin >> x) ได้

Loop Control Flow

break;

หยุดการทำงานของลูปทันทีและกระโดดออกไปทำคำสั่งหลังลูป

continue;

ข้ามการทำงานที่เหลือในรอบปัจจุบัน และเริ่มรอบถัดไปทันที

คำสั่งเหล่านี้ช่วยเพิ่มประสิทธิภาพและทำให้อัลกอริทึมซับซ้อนน้อยลง

ทบทวนการใช้ฟังก์ชัน (Functions)

int add(int a, int b) {

return a + b;

}

  • • ช่วยลดความซ้ำซ้อนของโค้ด (Code Reusability)
  • • ทำให้การ Debug ง่ายขึ้นเมื่อโค้ดมีความซับซ้อน
  • • ฟังก์ชันที่ทำงานเล็กๆ จะช่วยให้อัลกอริทึมหลักอ่านง่ายขึ้น

Pass by Reference (&)

By Value (Default)

จะมีการ Copy ข้อมูลใหม่ทั้งหมด
เปลืองหน่วยความจำและเวลาถ้าข้อมูลมีขนาดใหญ่

By Reference (&)

ส่ง "ตำแหน่ง" ของข้อมูลเดิมไป
รวดเร็วกว่าและสามารถแก้ไขค่าต้นฉบับได้

void change(int &x) { x = 0; }

ฟังก์ชันเรียกตัวเอง (Recursion)

Recursion คือฟังก์ชันที่เรียกใช้งานตัวเองเพื่อแก้ปัญหาย่อย (Subproblems)

int factorial(int n) {

if (n <= 1) return 1; // Base Case

return n * factorial(n - 1); // Recursive Step

}

ข้อควรระวัง: ต้องมี Base Case ที่ชัดเจนเสมอเพื่อป้องกัน Infinite Loop และ Stack Overflow

Recursive Efficiency

ปัญหาของการใช้ Recursion แบบปกติคือการคำนวณซ้ำ (Redundant Calculations)

ข้อเสีย:

- ทำงานช้าถ้ามีการเรียกซ้ำ
- ใช้หน่วยความจำ Stack สูง

วิธีแก้:

- ใช้ **Memoization** เพื่อเก็บค่าที่เคยคำนวณแล้ว
- หรือเปลี่ยนไปใช้วิธี **Iterative** (Loop)

Smart Recursion

long long memo[6]; // เก็บค่าผลลัพธ์

long long fib(int n) {

if (n <= 1) return n;

if (memo[n] != -1) return memo[n]; // คืนค่าถ้ามีอยู่แล้ว

return memo[n] = fib(n-1) + fib(n-2);

}

เทคนิคนี้เป็นหัวใจของ **Dynamic Programming** ซึ่งจะเรียนลึกขึ้นในวันที่ 5

Variable Scope

Global Variables

ประกาศนอกฟังก์ชัน เข้าถึงได้จากทุกที่ อาร์เรย์ที่ประกาศเป็น Global จะถูกล้างค่าเป็น 0 โดยอัตโนมัติ

Local Variables

ประกาศในฟังก์ชัน เข้าถึงได้เฉพาะในฟังก์ชันนั้น ค่าเริ่มต้นจะเป็นค่าขยะ (Random) หากไม่กำหนดค่า

const Keyword

ใช้สำหรับประกาศค่าคงที่ไม่เปลี่ยนแปลง เพื่อป้องกันข้อผิดพลาดและใช้กำหนดขนาดอาร์เรย์

const int MAXN = 100005;

const double PI = 3.1415926535;

MAXN = 200000; // Error! คอมไพล์ไม่ผ่าน

โครงสร้าง (struct)

ใช้จัดกลุ่มข้อมูลหลายประเภทที่เกี่ยวข้องกันไว้ด้วยกัน

struct Point {

double x, y;

};

Point p1 = {1.5, 2.0};

มีประโยชน์อย่างมากในการเก็บข้อมูลโจทย์เรขาคณิตหรือสถานะของโหนดในกราฟ

Member Functions ใน struct

struct Point {

double x, y;

void mirror() { y = -y; } // เปลี่ยนแปลงค่าในตัว

double dist() const { return sqrt(x*x + y*y); }

};

Keyword const หลังฟังก์ชันช่วยรับประกันว่าฟังก์ชันนี้จะไม่แก้ไขค่าในสมาชิก

ตัวสร้างโครงสร้าง (Constructors)

ช่วยให้กำหนดค่าเริ่มต้นให้ struct ได้อย่างรวดเร็ว

struct Edge {

int to, weight;

Edge(int t, int w) : to(t), weight(w) {}

};

adj[u].push_back(Edge(v, w));

Boolean logic (bool)

ใช้เก็บค่าจริง (true) หรือเท็จ (false) สำหรับตรวจสอบเงื่อนไข

! (NOT)
&& (AND)
|| (OR)
Competitive Tip: โจทย์บางข้อสามารถใช้ bitset แทนอาร์เรย์ bool เพื่อประหยัดพื้นที่หน่วยความจำได้ถึง 32 เท่า

Character (char)

เก็บตัวอักษร 1 ตัว (8 bits) มีค่าเป็นรหัส ASCII

char c = 'A';

cout << (int)c; // พิมพ์ค่า 65

if (c >= 'A' && c <= 'Z') // ตรวจสอบตัวพิมพ์ใหญ่

จำไว้ว่า '0' ไม่ได้มีค่าเท่ากับ 0 (รหัส ASCII ของ '0' คือ 48)

std::string

จัดการข้อความได้เหมือนอาร์เรย์ของตัวอักษร

  • s.size(): คืนค่าความยาวข้อความ
  • s + t: เชื่อมต่อข้อความสองอัน
  • s[i]: เข้าถึงตัวอักษรตำแหน่งที่ i (เริ่มที่ 0)
string s = "monkey";
cout << s.size(); // 6

getline Function

ใช้เมื่อต้องการอ่านข้อมูลที่มีเว้นวรรคจนจบทั้งบรรทัด

string s;

getline(cin, s);

ข้อควรระวัง: หากใช้ cin มาก่อน ต้องใช้ cin.ignore() เพื่อล้างตัวอักษรขึ้นบรรทัดใหม่ (\n) ก่อนเรียก getline

<cmath>

abs(x)

ค่าสัมบูรณ์

sqrt(x)

รากที่สอง

pow(x, y)

x ยกกำลัง y

sin, cos, tan

ตรีโกณมิติ

ceil(x)

ปัดขึ้น

floor(x)

ปัดลง

ฟังก์ชันเหล่านี้ทำงานกับ double หรือ float

min() & max()

อยู่ในไลบรารี <algorithm> ใช้เปรียบเทียบค่าสองค่า

cout << min(3, 7); // 3

cout << max(10, 5); // 10

cout << min({a, b, c, d}); // (C++11) หาค่าต่ำสุดจากลิสต์

Overflow Checklist

  • 🚨 คำนวณผลรวม (Sum) ของอาร์เรย์ที่สมาชิกเป็น int: ผลรวมอาจเกิน 2 พันล้าน
  • 🚨 การคูณเลขสองตัว (a * b): แม้ผลลัพธ์สุดท้ายจะใส่ long long แต่ถ้า a และ b เป็น int ผลลัพธ์ระหว่างทางอาจ Overflow ไปแล้ว!
ทางเลือกที่ปลอดภัย: ใช้ long long ไว้ก่อนถ้าไม่มั่นใจเรื่องขนาด

The endl Bottleneck

เหตุผลที่ endl ช้ากว่า "\n" อย่างมาก

cout << endl;

1. ขึ้นบรรทัดใหม่
2. Flush Buffer (บังคับส่งข้อมูลลงจอทันที)

cout << "\n";

1. ขึ้นบรรทัดใหม่เท่านั้น (รวบรวมข้อมูลไว้ส่งทีเดียวเมื่อบัฟเฟอร์เต็ม)

สำคัญมากในโจทย์ที่ต้องแสดงผลข้อมูลนับแสนบรรทัด

assert() Function

ใช้ตรวจสอบสมมติฐาน (Assumption) ระหว่างเขียนโค้ด

#include <cassert>

assert(n <= 100); // ถ้า n > 100 โปรแกรมจะ Crash ทันที

  • • ช่วยตรวจหาบั๊กก่อนส่ง (เช่น ตรวจสอบว่า Index อาร์เรย์ไม่ติดลบ)
  • • เมื่อส่งไปยัง Online Judge ฟังก์ชันนี้มักจะทำให้เกิด Runtime Error (RE) แทนที่จะเป็น Wrong Answer (WA)

Catch Silly Mistakes

ตั้งค่า Compiler ให้แจ้งเตือนข้อผิดพลาดที่มองไม่เห็นด้วยตาเปล่า

-Wall

เปิดการแจ้งเตือนทั่วไป เช่น ตัวแปรที่ประกาศไว้แต่ไม่ได้ใช้งาน (Unused Variables)

-Wconversion

เตือนเมื่อมีการแปลงชนิดข้อมูลที่อาจทำให้ค่าเปลี่ยน (Implicit Conversion)

g++ -Wall -Wextra -O2 main.cpp

ความเร็วในการพิมพ์สำคัญไหม?

"Don’t focus on typing speed as a beginner."

  • • หัวใจของการแข่งขันคือ การแก้ปัญหา (Solving problems) ไม่ใช่การพิมพ์เร็ว
  • • โปรแกรมเมอร์ระดับโลกบางคนพิมพ์ด้วย 2 นิ้ว แต่คิดอัลกอริทึมได้แม่นยำ
  • • ใช้เวลา 1-2 นาทีคิดโครงสร้างโค้ดที่ดี ดีกว่ารีบพิมพ์แล้วต้องมาไล่แก้บั๊ก

คู่มือคู่ใจ: CP Handbook

Competitive Programmer's Handbook (CSES)

เป็นแหล่งข้อมูลฟรีที่ครอบคลุมเนื้อหาตั้งแต่นักเรียนใหม่จนถึงระดับกลาง รวบรวมทั้งการพิสูจน์ (Proofs) และตัวอย่างโค้ด (Implementations) ที่ใช้งานได้จริง

cses.fi/book.pdf
  • • มีหัวข้อที่เรียงลำดับได้ดีมาก
  • • เหมาะกับการอ่านทบทวนก่อนแข่ง
  • • มีโจทย์ชุด CSES Problem Set ให้ฝึกทำควบคู่กัน

กลยุทธ์สำคัญสำหรับผู้เริ่มต้น

ไม่ต้องเน้นความเร็วในการพิมพ์ในช่วงแรก ความถูกต้องสำคัญกว่า
📚
ศึกษาจาก Competitive Programmer's Handbook (CSES) สม่ำเสมอ
🧪
ฝึกไล่บั๊กบนกระดาษเมื่อคอมพิวเตอร์ถูกใช้งานโดยเพื่อนร่วมทีม

Summary (Morning Session)

  • • C++ คือภาษามาตรฐานเพื่อประสิทธิภาพสูงสุด
  • • Integer Overflow และ Floating Point คือจุดตาย
  • • ใช้ Fast I/O เสมอในโจทย์ข้อมูลขนาดใหญ่
  • • แยกโค้ดเป็นฟังก์ชันและใช้ Reference เพื่อลดความซับซ้อน
เตรียมตัวพบกับ Arrays และ Control Structures ในช่วงบ่าย!