[0]zero-index clytie0x/zero-index

13.6 แบบฝึกหัด

เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น

ข้อ 1 เข้าแถวตามส่วนสูง

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูพละให้นักเรียน N คนเข้าแถวหน้าเสาธงโดยเรียงจากคนที่เตี้ยที่สุดไปคนที่สูงที่สุด ครูจดส่วนสูงของทุกคนเป็นเซนติเมตรตามลำดับที่เดินมาถึง ให้พิมพ์ส่วนสูงเรียงตามลำดับในแถว

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 1000)

บรรทัดที่สองมีส่วนสูงของนักเรียน N จำนวน คั่นด้วยช่องว่าง แต่ละจำนวนเป็นจำนวนเต็มตั้งแต่ 100 ถึง 200

ข้อมูลออก

บรรทัดเดียว ส่วนสูงทั้ง N จำนวนเรียงจากน้อยไปมาก คั่นด้วยช่องว่างหนึ่งช่อง

6
152 148 160 155 149 171
148 149 152 155 160 171
5
160 155 160 148 155
148 155 155 160 160
ดูคำใบ้

N ไม่เกิน 1000 ใช้วิธีเรียงที่เขียนเองในหน้า 13.2 หรือ 13.3 ได้สบาย อ่านส่วนสูงทั้งหมดเข้าอาเรย์ก่อน เรียง แล้วค่อยพิมพ์ ส่วนสูงที่เท่ากันต้องพิมพ์ครบทุกคน ถ้าผลมีบางค่าซ้ำแปลกๆ หรือหายไป ให้ตรวจการสลับว่ามีตัวแปรพักแล้วหรือยัง

ดูเฉลย
#include <stdio.h>

int height[1000];

void insertion_sort(int a[], int n)
{
    for (int i = 1; i < n; i++) {
        int key = a[i];
        int j = i - 1;
        while (j >= 0 && a[j] > key) {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = key;
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &height[i]);
    }
    insertion_sort(height, n);
    for (int i = 0; i < n; i++) {
        if (i > 0) {
            printf(" ");
        }
        printf("%d", height[i]);
    }
    printf("\n");
    return 0;
}

ข้อ 2 เวลากลางของนักวิ่ง

ระดับง่าย เวลาไม่เกิน 1 วินาที

งานวิ่งการกุศลของโรงเรียนมีนักวิ่ง N คน เครื่องจับเวลาบันทึกเวลาที่แต่ละคนใช้เป็นวินาที ตามลำดับเลขบิบ ไม่ได้เรียงตามเวลา ผู้จัดอยากประกาศว่านักวิ่ง "ทั่วไป" ใช้เวลาเท่าไร จึงต้องการค่ากลาง คือเวลาของคนที่อยู่ตรงกลางพอดีเมื่อเรียงเวลาของทุกคนจากน้อยไปมาก N เป็นเลขคี่เสมอ

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็มคี่ N (1 ≤ N ≤ 999)

บรรทัดที่สองมีเวลาของนักวิ่ง N คน คั่นด้วยช่องว่าง แต่ละค่าเป็นจำนวนเต็มตั้งแต่ 1 ถึง 100000

ข้อมูลออก

ค่ากลางของเวลาทั้ง N ค่า

5
1820 1795 2410 1650 3600
1820
7
2100 1980 2100 2650 1700 2100 1990
2100
ดูคำใบ้

ข้อมูลยังไม่เรียง ต้องเรียงก่อน แล้วค่ากลางคือช่อง N / 2 ตามหน้า 13.1 ถ้าตอบช่อง N / 2 ของข้อมูลที่ยังไม่เรียง จะผ่านเฉพาะชุดที่บังเอิญเรียงมาแล้ว

ดูเฉลย
#include <stdio.h>

int seconds[1000];

void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

void selection_sort(int a[], int n)
{
    for (int i = 0; i < n - 1; i++) {
        int min_index = i;
        for (int j = i + 1; j < n; j++) {
            if (a[j] < a[min_index]) {
                min_index = j;
            }
        }
        swap(&a[i], &a[min_index]);
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &seconds[i]);
    }
    selection_sort(seconds, n);
    printf("%d\n", seconds[n / 2]);
    return 0;
}

ข้อ 3 ห้องเก็บของหาย

ระดับง่าย เวลาไม่เกิน 1 วินาที

ห้องเก็บของหายของโรงเรียนวางกล่องเรียงเป็นแถวยาว N กล่อง กล่องที่ 1 อยู่ซ้ายสุด แต่ละกล่องติดป้ายเลขรหัสของสิ่งของในกล่อง ของหลายชิ้นอาจมีรหัสเดียวกันได้ เช่น ขวดน้ำรุ่นเดียวกัน นักเรียนคนหนึ่งมาถามหาของรหัส X ให้บอกว่ามีกล่องที่ติดรหัส X กี่กล่อง และอยู่กล่องที่เท่าไรบ้าง

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N และ X คั่นด้วยช่องว่าง (1 ≤ N ≤ 1000 และ 1 ≤ X ≤ 109)

บรรทัดที่สองมีรหัสบนกล่องที่ 1 ถึงกล่องที่ N คั่นด้วยช่องว่าง แต่ละรหัสเป็นจำนวนเต็มตั้งแต่ 1 ถึง 109

ข้อมูลออก

บรรทัดแรกพิมพ์จำนวนกล่องที่ติดรหัส X

ถ้าจำนวนนั้นมากกว่า 0 บรรทัดที่สองพิมพ์ลำดับของกล่องเหล่านั้นจากซ้ายไปขวา คั่นด้วยช่องว่างหนึ่งช่อง ถ้าไม่มีกล่องใดเลย ให้พิมพ์แค่บรรทัดแรก

6 870
1024 5531 870 4410 870 9902
2
3 5
4 77
12 34 56 78
0
ดูคำใบ้

ค้นหาแบบไล่ในหน้า 13.5 แต่ต้องไล่ให้ครบทุกกล่อง ห้ามหยุดที่กล่องแรกที่เจอ จำนวนต้องพิมพ์ก่อนลำดับกล่อง จึงต้องรู้จำนวนก่อน จะไล่สองรอบ หรือเก็บลำดับที่เจอไว้ในอาเรย์ก็ได้ ลำดับกล่องเริ่มนับจาก 1 ไม่ใช่ 0 และอย่าพิมพ์ว่าไม่เจอไว้ในลูป

ดูเฉลย
#include <stdio.h>

int code[1000];
int found[1000];

int main()
{
    int n, target;
    scanf("%d %d", &n, &target);
    for (int i = 0; i < n; i++) {
        scanf("%d", &code[i]);
    }
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (code[i] == target) {
            found[count] = i + 1;
            count++;
        }
    }
    printf("%d\n", count);
    if (count > 0) {
        for (int i = 0; i < count; i++) {
            if (i > 0) {
                printf(" ");
            }
            printf("%d", found[i]);
        }
        printf("\n");
    }
    return 0;
}

ข้อ 4 ล่าคำในบทความ

ระดับกลาง เวลาไม่เกิน 1 วินาที

น้องกำลังทำรายงาน และอยากรู้ว่าคำที่สนใจปรากฏในบทความบ่อยแค่ไหน บทความพิมพ์มาทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กปนกัน น้องจึงอยากให้นับโดยไม่สนตัวพิมพ์ เช่น the The และ THE ถือว่าตรงกันทั้งหมด

ให้นับว่ามีกี่ตำแหน่งในบทความที่ถ้าเริ่มอ่านจากตำแหน่งนั้นแล้วจะได้คำที่ค้นหา คำที่ค้นหาอาจอยู่ในคำอื่น เช่น the ใน other ก็นับ และตำแหน่งที่พบอาจซ้อนกันได้ เช่น ana ใน banana นับเป็น 2 ครั้ง

ข้อมูลเข้า

บรรทัดแรกคือบทความ ยาว 1 ถึง 1000 ตัวอักษร ประกอบด้วยตัวอักษรภาษาอังกฤษพิมพ์ใหญ่หรือพิมพ์เล็ก และช่องว่าง

บรรทัดที่สองคือคำที่ค้นหา ยาว 1 ถึง 100 ตัวอักษร ประกอบด้วยตัวอักษรภาษาอังกฤษพิมพ์ใหญ่หรือพิมพ์เล็กเท่านั้น

ข้อมูลออก

บรรทัดเดียว มีจำนวนครั้งที่พบ ตามด้วยช่องว่าง แล้วตามด้วยตำแหน่งแรกที่พบ โดยตัวอักษรตัวแรกของบทความคือตำแหน่งที่ 1 ถ้าไม่พบเลยให้พิมพ์ 0 -1

The cat sat on THE mat with the other cat
the
4 1
BaNaNa bandana
ANA
3 2
ดูคำใบ้

ใช้การหาคำแบบลองทุกตำแหน่งเริ่มในหน้า 13.5 แต่ตอนเทียบทีละตัวให้แปลงทั้งสองฝั่งเป็นตัวพิมพ์เล็กก่อน ด้วย tolower จาก ctype.h ในหน้า 6.1 อ่านทั้งสองบรรทัดด้วย fgets และตัด '\n' ทิ้งทั้งคู่ คำที่ค้นหาอาจยาวกว่าบทความ ซึ่งต้องตอบ 0 -1 ตำแหน่งที่พิมพ์เริ่มนับจาก 1

ดูเฉลย
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char text[1005], word[105];

void strip_newline(char s[])
{
    for (int i = 0; s[i] != '\0'; i++) {
        if (s[i] == '\n' || s[i] == '\r') {
            s[i] = '\0';
        }
    }
}

int main()
{
    fgets(text, sizeof text, stdin);
    fgets(word, sizeof word, stdin);
    strip_newline(text);
    strip_newline(word);
    int n = strlen(text);
    int m = strlen(word);
    int count = 0;
    int first = -1;
    for (int i = 0; i + m <= n; i++) {
        int j = 0;
        while (j < m && tolower(text[i + j]) == tolower(word[j])) {
            j++;
        }
        if (j == m) {
            if (count == 0) {
                first = i + 1;
            }
            count++;
        }
    }
    printf("%d %d\n", count, first);
    return 0;
}

ข้อ 5 ทะเบียนรถที่ด่าน

ระดับกลาง เวลาไม่เกิน 1 วินาที

กล้องที่ด่านเก็บค่าผ่านทางอ่านทะเบียนรถทุกคันที่ผ่านในวันหนึ่ง แล้วแปลงเป็นรหัสตัวเลข รถคันเดียวกันได้รหัสเดิมทุกครั้ง และรถบางคันผ่านด่านหลายรอบ วันนี้กล้องอ่านได้ N ครั้ง ให้หาว่ามีรถที่ต่างกันกี่คัน และรถคันที่ผ่านบ่อยที่สุดผ่านกี่ครั้ง

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 100000)

บรรทัดที่สองมีรหัสที่อ่านได้ N รหัสตามลำดับเวลา คั่นด้วยช่องว่าง แต่ละรหัสเป็นจำนวนเต็มตั้งแต่ 1 ถึง 109

ข้อมูลออก

บรรทัดเดียว มีจำนวนรถที่ต่างกัน ตามด้วยช่องว่าง แล้วตามด้วยจำนวนครั้งที่รถคันที่ผ่านบ่อยที่สุดผ่านด่าน

7
5521 120 5521 98000 120 5521 7
4 3
3
1000000000 1 999999999
3 1
ดูคำใบ้

รหัสใหญ่ถึง 109 ใช้อาเรย์นับแบบหน้า 5.3 ไม่ได้ ให้เรียงก่อน รหัสเดียวกันจะอยู่ติดกันเป็นกลุ่ม แล้วนับกลุ่มกับความยาวของกลุ่มแบบตัวอย่างราคาขนมในหน้า 13.1 N ถึงแสน bubble sort เทียบราว 5 × 109 ครั้ง ไม่ทัน ต้องใช้ qsort จากหน้า 13.4 อย่าลืมปิดกลุ่มสุดท้ายหลังจบลูป

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>

int plate[100000];

int compare_int(const void *p, const void *q)
{
    int x = *(const int *)p;
    int y = *(const int *)q;
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &plate[i]);
    }
    qsort(plate, n, sizeof(int), compare_int);
    int distinct = 0;
    int most = 0;
    int start = 0;
    for (int i = 1; i <= n; i++) {
        if (i == n || plate[i] != plate[i - 1]) {
            distinct++;
            if (i - start > most) {
                most = i - start;
            }
            start = i;
        }
    }
    printf("%d %d\n", distinct, most);
    return 0;
}

ข้อ 6 คะแนนของอันดับที่ k

ระดับกลาง เวลาไม่เกิน 1 วินาที

การสอบคัดเลือกเข้าโรงเรียนแห่งหนึ่งมีผู้สมัคร N คน คะแนนเต็ม 100 ผู้สมัครแต่ละคนได้คะแนนเป็นจำนวนเต็ม เมื่อเรียงคะแนนของทุกคนจากมากไปน้อย คนแรกในรายการคืออันดับที่ 1 คนถัดไปคืออันดับที่ 2 ไปเรื่อยๆ ถ้ามีคนได้คะแนนเท่ากัน ใครอยู่ก่อนก็ได้ เพราะคะแนนที่ตำแหน่งนั้นเท่าเดิม

โรงเรียนมีคำถาม Q ข้อ แต่ละข้อถามว่าผู้สมัครอันดับที่ k ได้คะแนนเท่าไร เช่น ถ้ารับ 300 คน ก็อยากรู้คะแนนของอันดับที่ 300 ซึ่งเป็นคะแนนต่ำสุดที่ยังได้เข้าเรียน

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง (1 ≤ N ≤ 200000 และ 1 ≤ Q ≤ 1000)

บรรทัดที่สองมีคะแนนของผู้สมัคร N คน คั่นด้วยช่องว่าง แต่ละคะแนนเป็นจำนวนเต็มตั้งแต่ 0 ถึง 100

อีก Q บรรทัด บรรทัดละหนึ่งจำนวนเต็ม k (1 ≤ k ≤ N)

ข้อมูลออก

Q บรรทัด บรรทัดที่ i คือคะแนนของผู้สมัครอันดับที่ k ของคำถามข้อที่ i

6 4
72 85 64 85 90 58
1
2
3
6
90
85
85
58
5 4
50 50 50 40 40
3
4
5
1
50
40
40
50
ดูคำใบ้

คะแนนมีแค่ 101 ค่า ใช้ counting sort ในหน้า 13.5 นับว่าแต่ละคะแนนมีกี่คน แล้วตอบคำถามโดยไล่คะแนนจาก 100 ลงมา สะสมจำนวนคนไปเรื่อยๆ คะแนนแรกที่จำนวนสะสมถึง k คือคำตอบ หรือจะสร้างรายการที่เรียงจากมากไปน้อยด้วย counting sort ก่อนแล้วตอบช่อง k - 1 ก็ได้ N ถึงสองแสน bubble sort ไม่ทัน

ดูเฉลย
#include <stdio.h>

int count[101];

int main()
{
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        int score;
        scanf("%d", &score);
        count[score]++;
    }
    for (int i = 0; i < q; i++) {
        int k;
        scanf("%d", &k);
        int passed = 0;
        int score = 100;
        while (passed + count[score] < k) {
            passed += count[score];
            score--;
        }
        printf("%d\n", score);
    }
    return 0;
}

ข้อ 7 ตารางอันดับ

ระดับยาก เวลาไม่เกิน 1 วินาที

การแข่งขันเขียนโปรแกรมของโรงเรียนมีผู้เข้าแข่ง N คน เมื่อหมดเวลา แต่ละคนมีจำนวนข้อที่ทำผ่าน และเวลาปรับ (penalty) เป็นนาที กรรมการจัดตารางอันดับตามกติกานี้

  • ทำผ่านมากข้อกว่า อยู่ก่อน
  • ถ้าทำผ่านเท่ากัน เวลาปรับน้อยกว่า อยู่ก่อน
  • ถ้าทั้งสองอย่างเท่ากัน ถือว่าได้อันดับเดียวกัน แต่ในตารางให้เรียงตามชื่อตามลำดับพจนานุกรม

อันดับของแต่ละคนคือ 1 บวกจำนวนคนที่ทำได้ดีกว่าคนนั้นจริงๆ เช่น ถ้ามีสองคนเสมอกันที่อันดับ 2 คนถัดไปจะได้อันดับ 4 ไม่ใช่ 3

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 50000)

อีก N บรรทัด บรรทัดละหนึ่งคน มีชื่อ จำนวนข้อที่ทำผ่าน และเวลาปรับ คั่นด้วยช่องว่าง ชื่อเป็นตัวพิมพ์เล็กภาษาอังกฤษยาว 1 ถึง 10 ตัว และไม่มีชื่อซ้ำกัน จำนวนข้อเป็นจำนวนเต็มตั้งแต่ 0 ถึง 10 เวลาปรับเป็นจำนวนเต็มตั้งแต่ 0 ถึง 100000

ข้อมูลออก

N บรรทัดตามลำดับในตาราง บรรทัดละหนึ่งคน มีอันดับ ตามด้วยช่องว่าง แล้วตามด้วยชื่อ

6
somchai 3 120
malee 4 300
anan 3 95
kanya 3 120
preeda 1 10
boonmee 3 120
1 malee
2 anan
3 boonmee
3 kanya
3 somchai
6 preeda
2
zed 0 0
amy 0 0
1 amy
1 zed
ดูคำใบ้

เก็บแต่ละคนเป็น struct แล้วเรียงด้วย qsort ตามสามเงื่อนไขแบบหน้า 13.4 เงื่อนไขแรกที่ต่างกันเป็นตัวตัดสิน ห้ามลบกันในฟังก์ชันเปรียบเทียบ หลังเรียงแล้วไล่คำนวณอันดับ คนแรกได้อันดับ 1 คนต่อไปถ้าจำนวนข้อและเวลาปรับเท่ากับคนก่อนหน้าให้ได้อันดับเดียวกับคนก่อนหน้า ไม่อย่างนั้นได้อันดับเท่ากับตำแหน่งของตัวเองในตาราง (เริ่มนับจาก 1) N ถึงห้าหมื่น bubble sort ไม่ทัน

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct contestant {
    char name[11];
    int solved;
    int penalty;
};

struct contestant list[50000];

int compare_rank(const void *p, const void *q)
{
    const struct contestant *a = p;
    const struct contestant *b = q;
    if (a->solved != b->solved) {
        return a->solved > b->solved ? -1 : 1;
    }
    if (a->penalty != b->penalty) {
        return a->penalty < b->penalty ? -1 : 1;
    }
    return strcmp(a->name, b->name);
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s %d %d", list[i].name, &list[i].solved, &list[i].penalty);
    }
    qsort(list, n, sizeof(list[0]), compare_rank);
    int rank = 0;
    for (int i = 0; i < n; i++) {
        if (i == 0 || list[i].solved != list[i - 1].solved || list[i].penalty != list[i - 1].penalty) {
            rank = i + 1;
        }
        printf("%d %s\n", rank, list[i].name);
    }
    return 0;
}

ข้อ 8 สถานีที่ตรวจพบใกล้กันที่สุด

ระดับยาก เวลาไม่เกิน 1 วินาที

เครือข่ายเฝ้าระวังแผ่นดินไหวมีสถานีตรวจวัด N สถานี แต่ละสถานีบันทึกเวลาที่ตรวจพบคลื่นเป็นมิลลิวินาที เทียบกับเวลาอ้างอิงของเครือข่าย ค่าติดลบคือก่อนเวลาอ้างอิง นักวิจัยอยากรู้ว่าสองสถานีที่ตรวจพบใกล้กันที่สุดห่างกันกี่มิลลิวินาที และมีสถานีกี่คู่ที่ห่างกันเท่านั้นพอดี

ระยะห่างของสถานี i กับสถานี j คือค่าสัมบูรณ์ของผลต่างของเวลา คู่ (i, j) กับ (j, i) นับเป็นคู่เดียวกัน และสองสถานีอาจตรวจพบในเวลาเดียวกันได้

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (2 ≤ N ≤ 100000)

บรรทัดที่สองมีเวลาของสถานีที่ 1 ถึงสถานีที่ N คั่นด้วยช่องว่าง แต่ละค่าเป็นจำนวนเต็มตั้งแต่ -2 × 109 ถึง 2 × 109

ข้อมูลออก

บรรทัดเดียว มีระยะห่างที่น้อยที่สุด ตามด้วยช่องว่าง แล้วตามด้วยจำนวนคู่สถานีที่ห่างกันเท่ากับค่านั้น

6
350 -120 900 410 15 470
60 2
5
8 3 8 3 8
0 4
ดูคำใบ้

เทียบทุกคู่ต้องทำราว 5 × 109 ครั้ง ไม่ทัน ถ้าเรียงแล้ว คู่ที่ใกล้กันที่สุดต้องอยู่ติดกันเสมอ ดูแค่ N - 1 คู่ที่ติดกันพอ มีสามจุดที่ต้องระวัง หนึ่ง ค่าถึง ±2 × 109 ฟังก์ชันเปรียบเทียบของ qsort ห้ามใช้ return x - y ตามหน้า 13.4 สอง ผลต่างอาจถึง 4 × 109 ต้องคำนวณเป็น long long และสาม ถ้าระยะน้อยสุดเป็น 0 สถานีที่เวลาเท่ากัน c สถานีจับคู่กันได้ c × (c - 1) / 2 คู่ ไม่ใช่แค่คู่ที่ติดกัน และจำนวนคู่อาจเกิน int

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>

int when[100000];

int compare_int(const void *p, const void *q)
{
    int x = *(const int *)p;
    int y = *(const int *)q;
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &when[i]);
    }
    qsort(when, n, sizeof(int), compare_int);
    long long best = (long long)when[1] - when[0];
    for (int i = 2; i < n; i++) {
        long long gap = (long long)when[i] - when[i - 1];
        if (gap < best) {
            best = gap;
        }
    }
    long long pairs = 0;
    if (best > 0) {
        for (int i = 1; i < n; i++) {
            if ((long long)when[i] - when[i - 1] == best) {
                pairs++;
            }
        }
    } else {
        int start = 0;
        for (int i = 1; i <= n; i++) {
            if (i == n || when[i] != when[i - 1]) {
                long long size = i - start;
                pairs += size * (size - 1) / 2;
                start = i;
            }
        }
    }
    printf("%lld %lld\n", best, pairs);
    return 0;
}

ข้อ 9 คู่สลับอักษร

ระดับยาก เวลาไม่เกิน 1 วินาที

ชมรมภาษาอังกฤษจัดเกมสลับอักษร นักเรียนส่งคำมาทั้งหมด N คำ สองคำเป็นคู่สลับอักษร (anagram) ถ้าใช้ตัวอักษรชุดเดียวกัน แต่ละตัวมีจำนวนเท่ากัน แค่เรียงคนละลำดับ เช่น listen กับ silent หรือ stop กับ pots แต่ lose กับ loose ไม่ใช่ เพราะจำนวนตัว o ไม่เท่ากัน คำที่เหมือนกันทุกตัวก็ถือว่าเป็นคู่สลับอักษรกัน

ให้นับว่ามีกี่คู่ (i, j) ที่ i < j และคำที่ i กับคำที่ j เป็นคู่สลับอักษรกัน

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 100000)

อีก N บรรทัด บรรทัดละหนึ่งคำ แต่ละคำเป็นตัวพิมพ์เล็กภาษาอังกฤษยาว 1 ถึง 8 ตัว

ข้อมูลออก

จำนวนคู่ที่เป็นคู่สลับอักษร

7
listen
google
silent
stop
enlist
pots
tops
6
7
lose
loose
sole
cat
act
tac
dog
4
ดูคำใบ้

สองคำเป็นคู่สลับอักษรกันก็ต่อเมื่อเรียงตัวอักษรในคำแล้วได้คำเดียวกัน เช่น listen และ silent เรียงแล้วได้ eilnst ทั้งคู่ คำยาวไม่เกิน 8 ตัว เรียงตัวอักษรด้วย insertion sort ได้สบาย จากนั้นเรียงคำที่ได้ทั้งหมดด้วย qsort กับ strcmp แบบหน้า 13.4 คำที่เหมือนกันจะอยู่ติดกัน กลุ่มที่มี c คำจับคู่ได้ c × (c - 1) / 2 คู่ ผลรวมอาจเกิน int

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char key[100000][9];

void sort_letters(char s[])
{
    int length = strlen(s);
    for (int i = 1; i < length; i++) {
        char letter = s[i];
        int j = i - 1;
        while (j >= 0 && s[j] > letter) {
            s[j + 1] = s[j];
            j--;
        }
        s[j + 1] = letter;
    }
}

int compare_key(const void *p, const void *q)
{
    return strcmp((const char *)p, (const char *)q);
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", key[i]);
        sort_letters(key[i]);
    }
    qsort(key, n, sizeof(key[0]), compare_key);
    long long pairs = 0;
    int start = 0;
    for (int i = 1; i <= n; i++) {
        if (i == n || strcmp(key[i], key[i - 1]) != 0) {
            long long size = i - start;
            pairs += size * (size - 1) / 2;
            start = i;
        }
    }
    printf("%lld\n", pairs);
    return 0;
}

ข้อ 10 ต่อบัตรให้ได้เลขมากที่สุด

ระดับยาก เวลาไม่เกิน 1 วินาที

เกมบัตรตัวเลขมีบัตร N ใบ แต่ละใบเขียนจำนวนเต็มไม่ติดลบไว้หนึ่งจำนวน ผู้เล่นต้องวางบัตรทุกใบเรียงต่อกันเป็นแถวเดียว แล้วอ่านตัวเลขทั้งแถวติดกันเป็นจำนวนเดียว เช่น บัตร 30 5 34 วางเป็น 5 34 30 จะได้ 53430 ให้หาจำนวนที่มากที่สุดที่วางได้

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 100000)

บรรทัดที่สองมีตัวเลขบนบัตร N ใบ คั่นด้วยช่องว่าง แต่ละจำนวนเป็นจำนวนเต็มตั้งแต่ 0 ถึง 109 เขียนโดยไม่มีเลข 0 นำหน้า

ข้อมูลออก

จำนวนที่มากที่สุด พิมพ์โดยไม่มีเลข 0 นำหน้า ถ้าจำนวนนั้นเป็นศูนย์ให้พิมพ์ 0 ตัวเดียว

5
30 5 34 3 9
9534330
2
10 2
210
ดูคำใบ้

ผลยาวได้ถึงล้านหลัก เก็บเป็นตัวเลขไม่ได้ ให้รับบัตรแต่ละใบเป็นสตริง แล้วคิดว่าบัตร a ควรวางก่อนบัตร b เมื่อไร การเรียงตามค่าตัวเลขผิด เพราะ 9 ต้องมาก่อน 30 ส่วนการเรียงตาม strcmp อย่างเดียวก็ผิด ลองบัตร 3 กับ 30 ดู ให้ลองต่อทั้งสองแบบ คือ a ตามด้วย b และ b ตามด้วย a แบบไหนได้มากกว่าก็เลือกแบบนั้น สองแบบยาวเท่ากันจึงเทียบด้วย strcmp ได้เลย เขียนเป็นฟังก์ชันเปรียบเทียบของ qsort ตามหน้า 13.4 แล้วอย่าลืมกรณีที่บัตรเป็น 0 ทุกใบ

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char card[100000][11];

int compare_card(const void *p, const void *q)
{
    const char *a = p;
    const char *b = q;
    char ab[21], ba[21];
    strcpy(ab, a);
    strcat(ab, b);
    strcpy(ba, b);
    strcat(ba, a);
    return strcmp(ba, ab);
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", card[i]);
    }
    qsort(card, n, sizeof(card[0]), compare_card);
    if (strcmp(card[0], "0") == 0) {
        printf("0\n");
        return 0;
    }
    for (int i = 0; i < n; i++) {
        printf("%s", card[i]);
    }
    printf("\n");
    return 0;
}