Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

C++ หา Subset ที่หารลงตัวได้มากที่สุดในอาร์เรย์ (Largest Divisible Subset)


บทช่วยสอนนี้จะอภิบัติปัญหาที่เราได้รับอาร์เรย์ของจำนวนเต็มบวกที่ไม่ซ้ำกัน เราต้องหา Subset ใหญ่ที่สุด ที่สำหรับทุกคู่ สมาชิกที่ใหญ่กว่าหารด้วยสมาชิกที่เล็กกว่าลงตัว เช่น

Input: nums[ ] = { 1, 4, 2, 6, 7} Output: 1 2 4 Explanation: Subset ที่หารลงตัวได้หมดคือ: (1, 2, 4), (1, 2, 6), (1, 7) ฯลฯ มี 2 subset ขนาด 3 ที่เงื่อนไขเป็นจริง
Input: nums[ ] = { 1, 2, 3, 6 } Output: 6 2 1

วิธีการแก้ปัญหา

มีสองวิธีที่แตกต่างกันที่เราจะอธิบายในบทช่วยสอนนี้

วิธีง่าย (Simple Approach)

ในวิธีง่าย เราสามารถใช้ Recursion แก้ปัญหา เราจะพิจารณาแต่ละสมาชิกว่าควรรวมใน Subset หรือไม่ สมมติเริ่มจากสมาชิกแรก เรามี 2 ทางเลือก: รวมหรือไม่รวม ถ้ารวมสมาชิกแรก สำหรับสมาชิกที่สองให้รวม ต้องหารลงตัวหรือถูกหารด้วยสมาชิกใน Substring คือสมาชิกแรก เราจะดำเนินต่อไปจนจบอาร์เรย์ จะมี 2^n เส้นทาง สร้าง Time Complexity O(2^n) มาดูวิธีที่เป็นไปได้มากกว่า

วิธีมีประสิทธิภาพ (Efficient Approach)

วิธีที่มีประสิทธิภาพสามารถใช้ Dynamic Programming ได้

  • จัดเรียงอาร์เรย์เพื่อให้สมาชิกซ้ายหารด้วยสมาชิกขวาลงตัว ต้องเช็ค divisibility แค่ครั้งเดียว
  • เราจะใช้ Longest Increasing Subsequence (LIS) คืออาร์เรย์ dp[] เพื่อเก็บ Largest Divisible Subset จนถึงดัชนี i เริ่มต้นแต่ละดัชนีเป็น 1 เพราะทุกตัวหารตัวเองลงตัว
  • วนลูปจากดัชนีที่ 2 ตรวจสอบแต่ละสมาชิกหา Maximum Divisible Subset ที่ลงท้ายด้วยดัชนีปัจจุบัน
  • วนลูปผ่านอาร์เรย์ สำหรับแต่ละสมาชิก หาตัวหารที่มี Divisible Count มากที่สุด แล้วเปลี่ยนค่า Divisible Count ของดัชนีปัจจุบันเป็นค่านั้น + 1

ตัวอย่าง

โค้ด C++ สำหรับวิธีการข้างต้น

#include<bits/stdc++.h> 
using namespace std; 

int main(){ 
    int nums[] = {1, 2, 3, 6}; 
    int n = sizeof(nums)/sizeof(int); 
    sort(nums, nums+n); 
    vector <int> prev_res(n, -1); 
    vector <int> dp(n, 1); 
    int max = 1; 
    for (int i=1; i<n; i++){ 
        for (int j=0; j<i; j++){ 
            if (nums[i]%nums[j] == 0){ 
                if (dp[i] < dp[j] + 1){ 
                    dp[i] = dp[j]+1; 
                    prev_res[i] = j; 
                } 
            } 
        } 
        if(max < dp[i]) max = dp[i]; 
    } 
    cout << "Largest divisible subset in the array: "; 
    int k = max; 
    while (k >= 0){ 
        cout << nums[k] << " "; 
        k = prev_res[k]; 
    } 
    return 0; 
}

ผลลัพธ์

Largest divisible subset in the array: 6 2 1

สรุป

ในบทช่วยสอนนี้ เราอภิบัติปัญหา: ต้องหา Largest Divisible Subset ในอาร์เรย์ที่กำหนด ซึ่งจำนวนเต็มในแต่ละคู่หารลงตัว เราอภิบัติวิธีจาก Recursion ที่สร้าง Complexity แบบ Exponential จึงอภิบัติวิธี Dynamic Programming เรายังอภิบัติโปรแกรม C++ สำหรับปัญหานี้ ซึ่งสามารถทำได้กับภาษาอื่น เช่น C, Java, Python ฯลฯ หวังว่าจะเป็นประโยชน์