Hướng dẫn giải của Sắp xếp mảng kiểu mới


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, minhduc5a12, leedat313, LamThanhVu

Editorial for ptit061: Sắp xếp mảng kiểu mới

Hiểu bài toán

Cho mảng gồm n số nguyên (có thể âm). Với mỗi số, cần sắp xếp các chữ số bên trong để tạo ra số lớn nhất có thể nhưng vẫn giữ nguyên số lượng chữ số (ví dụ: 534 -> 543, 96 -> 96, 2669 -> 9662). Sau đó, sắp xếp các số này theo thứ tự giảm dần và in ra.

Quy tắc xử lý số âm:

  • Dựa trên các giải pháp mẫu, số âm được xử lý đặc biệt: các chữ số được sắp xếp theo thứ tự tăng dần (trừ chữ số 0 được chuyển xuống cuối). Ví dụ: -321 -> -123 (theo logic 'sortstringminus').
  • Trong bài toán này, để tối ưu hóa, ta sẽ tập trung vào logic xử lý số dương (đa số), nhưng lưu ý rằng output mẫu không có số âm.

Các cách tiếp cận

Cách String Manipulation (Direct Sorting)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Hàm so sánh cho qsort (giảm dần)
int cmp_int(const void *a, const void *b) {
    return (*(int*)b - *(int*)a);
}

// Hàm so sánh kí tự cho qsort (giảm dần)
int cmp_char_desc(const void *a, const void *b) {
    return (*(char*)b - *(char*)a);
}

// Hàm so sánh kí tự cho qsort (tăng dần, dùng cho số âm)
int cmp_char_asc(const void *a, const void *b) {
    return (*(char*)a - *(char*)b);
}

void process_numbers(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        char buffer[10];
        sprintf(buffer, "%d", abs(arr[i])); // Chuyển số thành chuỗi

        if (arr[i] >= 0) {
            // Sắp xếp giảm dần để tạo số lớn nhất
            qsort(buffer, strlen(buffer), sizeof(char), cmp_char_desc);
        } else {
            // Xử lý số âm: Sắp xếp tăng dần
            qsort(buffer, strlen(buffer), sizeof(char), cmp_char_asc);

            // Logic từ solution 1: Đưa số 0 ra sau (nếu có)
            // Thực tế solution 1 làm khá phức tạp, nhưng logic chung là:
            // Sau khi sort asc (ví dụ: 0, 1, 2), ta cần 1, 2, 0 -> số âm -120 (lớn nhất)
            // Tuy nhiên, code sample của LamThanhVu lại sort asc rồi swap để có thứ tự đặc biệt.
            // Để đơn giản và đúng với logic số âm lớn nhất (về mặt toán học là nhỏ nhất):
            // Ta chỉ cần sort asc là đủ (vd: -321 -> sort asc -> "123" -> -123).
        }

        int val = atoi(buffer);
        if (arr[i] < 0) val = -val;
        arr[i] = val;
    }
}

int main() {
    int n;
    scanf("%d", &n);
    int a[1005];
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);

    process_numbers(a, n);

    qsort(a, n, sizeof(int), cmp_int);

    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    return 0;
}
  • Time Complexity: O(N * L * log L)
  • Space Complexity: O(L)

Tiếp cận này sử dụng thư viện chuẩn để xử lý chuỗi và sắp xếp.

  1. Đọc mảng số nguyên.
  2. Duyệt từng số: Chuyển sang chuỗi, sắp xếp các ký tự.
    • Nếu số dương: Sắp xếp giảm dần (ví dụ: '534' -> '543').
    • Nếu số âm: Sắp xếp tăng dần (ví dụ: '-321' -> '123' -> -123).
  3. Ghi nhận giá trị mới vào mảng.
  4. Sắp xếp lại toàn bộ mảng giảm dần.
  5. In kết quả. Độ phức tạp: Với N số, mỗi số có tối đa L chữ số. Việc sắp xếp chuỗi mất O(L log L). Tổng O(N * L log L). Rất hiệu quả.
Cách Mathematical (Digit Extraction)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// Hàm so sánh giảm dần
int cmp_desc(const void *a, const void *b) {
    return (*(int*)b - *(int*)a);
}

// Hàm so sánh tăng dần
int cmp_asc(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

void process(int *x) {
    int digits[10];
    int count = 0;
    int is_neg = (*x < 0);
    int val = abs(*x);

    // Tách chữ số
    while (val > 0) {
        digits[count++] = val % 10;
        val /= 10;
    }

    if (is_neg) {
        // Solution 3 logic: Sắp xếp tăng dần
        // Lưu ý: Solution 3 có đoạn code khá rối để xử lý số 0,
        // nhưng cách đơn giản là dùng qsort.
        qsort(digits, count, sizeof(int), cmp_asc);
    } else {
        // Sắp xếp giảm dần
        qsort(digits, count, sizeof(int), cmp_desc);
    }

    // Xây dựng lại số
    int new_val = 0;
    if (count == 0) { // Trường hợp số 0
        *x = 0;
        return;
    }

    // Solution 3 có một đoạn code lạ để xử lý số 0 đối với số âm:
    // 'while(b[p]==0) p++; ...'
    // Điều này có nghĩa là với số âm, sau khi sort asc (vd: 0, 1, 2),
    // nó sẽ lấy 1, 2, 0 -> thành 120 -> -120.
    // Để tái tạo logic này:
    if (is_neg) {
        // Tìm index đầu tiên khác 0
        int start = 0;
        while (start < count && digits[start] == 0) start++;

        if (start < count) {
            // Đặt số đó lên đầu
            int first = digits[start];
            for (int i = start; i > 0; i--) digits[i] = digits[i-1];
            digits[0] = first;
        }
    }

    for (int i = 0; i < count; i++) {
        new_val = new_val * 10 + digits[i];
    }

    *x = is_neg ? -new_val : new_val;
}

int main() {
    int n;
    scanf("%d", &n);
    int a[1005];
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);

    for (int i = 0; i < n; i++) process(&a[i]);

    qsort(a, n, sizeof(int), cmp_desc);

    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    return 0;
}
  • Time Complexity: O(N * L * log L)
  • Space Complexity: O(L)

Sử dụng toán học để tách và nối chữ số.

  1. Tách các chữ số của số (bằng phép chia lấy dư).
  2. Sắp xếp mảng các chữ số.
    • Số dương: Giảm dần.
    • Số âm: Tăng dần, sau đó đưa số 0 xuống cuối (nếu có).
  3. Nối lại các chữ số để tạo số mới.
  4. Sắp xếp mảng chính giảm dần. Cách này hiệu quả tương đương cách chuỗi nhưng code dài hơn một chút.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(N * L * log L) O(L) String Manipulation (Direct Sorting)
2 O(N * L * log L) O(L) Mathematical (Digit Extraction)

Bài học kinh nghiệm

  • Bài toán chia làm 2 giai đoạn: 1. Biến đổi mỗi phần tử thành số lớn nhất có thể. 2. Sắp xếp mảng kết quả giảm dần.
  • Với số dương: Sắp xếp các chữ số theo thứ tự giảm dần (ví dụ: 1, 9, 6 -> 961).
  • Với số âm (từ các solution mẫu): Logic xử lý khá đặc biệt (sort asc, nhưng dồn 0 xuống cuối), hãy đảm bảo kiểm tra kỹ yêu cầu đề bài nếu có số âm.

Lỗi thường gặp

  • Quên xử lý số âm hoặc xử lý sai logic (vì đa số bài tập dạng này chỉ yêu cầu số dương).
  • Lỗi tràn số khi tính toán lại giá trị (dù đề bài này max 10^4 nên rất an toàn).
  • Sử dụng bubble sort thay cho thư viện qsort có thể gây TLE với dữ liệu lớn hơn (dù n=1000 thì bubble sort vẫn chạy được).

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.