Giải bài toán xếp ba lô (bài toán cái túi) với C++

Lập trình C++

Bài toán xếp ba lô (còn được biết đến với tên gọi bài toán cái túi) là một bài toán tối ưu hóa tổ hợp. Bài toán được đặt tên từ vấn đề chọn những gì quan trọng có thể nhét vừa vào trong một cái túi (với giới hạn khối lượng) để mang theo trong một chuyến đi. Các bài toán tương tự thường xuất hiện trong kinh doanh.

Bài toán: Một kẻ trộm đột nhập vào một cửa hiệu tìm thấy có n mặt hàng có trọng lượng và giá trị khác nhau, nhưng hắn chỉ mang theo một cái túi có sức chứa về trọng lượng tối đa là m. Vậy vấn đề đặt ra là tên kẻ trộm nên bỏ vào ba lô những món nào và số lượng bao nhiêu để đạt giá trị cao nhất trong khả năng mà hắn có thể mang đi được?

Yêu cầu: Hãy xác định tổng giá trị đồ vật lớn nhất mà tên trộm có thể mang đi được.

Dữ liệu: Vào từ tệp văn bản DRSEM.INP gồm:

  • Dòng 1: Ghi số nguyên dương n và m (n ≤ 20, m ≤ 109).
  • Trong n dòng tiếp theo, dòng thứ i ghi số nguyên dương ai và bi (ai, bi ≤ 109).

Dữ liệu vào đảm bảo có ít nhất một cách chọn đồ vật.

Kết quả: Ghi ra tệp văn bản DRSEM.OUT gồm một dòng duy nhất ghi tổng giá trị đồ vật lớn nhất mà tên trộm có thể mang đi được.

DRSEM.INP

DRSEM.OUT

Giải thích

5 17

5 3

2 4

3 6

6 2

8 5

22

 

Chọn đồ vật thứ tự 1, 3, 4, 5 có tổng khối lượng là 16 < 17 và tổng giá trị lớn nhất là 22.

Code tham khảo:

#include <iostream>
using namespace std;
int max(int x, int y) {
   return (x > y) ? x : y;
}
int tromDo(long long m, int a[], int b[], int n) {
   int i, t;
   int k[n + 1][m + 1];
   for (i = 0; i <= n; i++) {
      for (t = 0; t <= m; t++) {
         if (i == 0 || t == 0)
            k[i][t] = 0;
         else if (b[i - 1] <= t)
            k[i][t] = max(a[i - 1] + k[i - 1][t - b[i - 1]], k[i - 1][t]);
         else
        k[i][t] = k[i - 1][t];
      }
   }
   return k[n][m];
}
int main() {
    freopen("DRSEM.INP","r",stdin);
    freopen("DRSEM.OUT","w",stdout);
    int n; long long m;
    cin >> n >> m;
    int a[n], b[n];
    for (int i = 0; i < n; i++) {
      cin >> a[i];
      cin >> b[i];
    }
   cout << tromDo(m, a, b, n);
   return 0;
}

Ngoài cách sử dụng mảng, chúng ta có thể sử dụng Vector để xử lý bài toán trên.

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

using namespace std;

struct Item {
    int weight;
    int value;
};

int knapsack(int n, int m, const vector<Item>& items) {
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            if (items[i - 1].weight <= j) {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - items[i - 1].weight] + items[i - 1].value);
            }
            else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }

    return dp[n][m];
}

int main() {
    ifstream inputFile("DRSEM.INP");
    ofstream outputFile("DRSEM.OUT");

    int n, m;
    inputFile >> n >> m;

    vector<Item> items(n);
    for (int i = 0; i < n; i++) {
        inputFile >> items[i].weight >> items[i].value;
    }

    int maxTotalValue = knapsack(n, m, items);

    outputFile << maxTotalValue << endl;

    inputFile.close();
    outputFile.close();

    return 0;
}

Trong ví dụ trên, chương trình sử dụng đọc dữ liệu từ tệp DRSEM.INP và ghi kết quả vào tệp DRSEM.OUT. Đầu vào gồm số lượng đồ vật n, khối lượng tối đa m, và danh sách n đồ vật với trọng lượng và giá trị tương ứng. Chương trình sử dụng một bảng hai chiều dp để lưu trữ giá trị tối ưu cho mỗi trường hợp con của bài toán.

Kết quả là tổng giá trị lớn nhất mà tên trộm có thể mang đi được, được ghi vào tệp DRSEM.OUT.

Vui lòng tạo tệp DRSEM.INP và chứa dữ liệu đầu vào theo đúng định dạng để chạy chương trình trên. Sau khi chạy, bạn sẽ có kết quả trong tệp DRSEM.OUT.

Cách thứ hai là chúng ta sử dụng thuật toán quay lui. Để giải bài toán xếp ba lô bằng thuật toán quay lui, bạn có thể áp dụng một hàm đệ quy để thử tất cả các khả năng chọn hoặc không chọn từng vật phẩm. Dưới đây là một ví dụ về cách giải quyết bài toán này bằng thuật toán quay lui:

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

struct Item {
    int weight;
    int value;
};

vector<int> selectedItems; // Lưu trữ vật phẩm được chọn
int maxTotalValue; // Giá trị tối đa có thể đạt được

void knapsack(int currentItem, int currentWeight, int totalValue, const vector<Item>& items, int m) {
    if (currentItem == items.size() || currentWeight >= m) {
        if (totalValue > maxTotalValue) {
            maxTotalValue = totalValue;
            selectedItems.clear();
            for (int i = 0; i < currentItem; i++) {
                if (selectedItems[i])
                    selectedItems.push_back(i);
            }
        }
        return;
    }

    if (currentWeight + items[currentItem].weight <= m) {
        selectedItems[currentItem] = 1;
        knapsack(currentItem + 1, currentWeight + items[currentItem].weight, totalValue + items[currentItem].value, items, m);
    }

    selectedItems[currentItem] = 0;
    knapsack(currentItem + 1, currentWeight, totalValue, items, m);
}

int main() {
    ifstream inputFile("DRSEM.INP");
    ofstream outputFile("DRSEM.OUT");

    int n, m;
    inputFile >> n >> m;

    vector<Item> items(n);
    for (int i = 0; i < n; i++) {
        inputFile >> items[i].weight >> items[i].value;
    }

    selectedItems.resize(n);
    maxTotalValue = 0;

    knapsack(0, 0, 0, items, m);

    outputFile << maxTotalValue << endl;

    inputFile.close();
    outputFile.close();

    return 0;
}

Trong ví dụ trên, chương trình sử dụng đọc dữ liệu từ tệp DRSEM.INP và ghi kết quả vào tệp DRSEM.OUT. Đầu vào gồm số lượng đồ vật n, khối lượng tối đa m, và danh sách n đồ vật với trọng lượng và giá trị tương ứng. Chương trình sử dụng một mảng selectedItems để lưu trữ các vật phẩm được chọn và một biến maxTotalValue để lưu trữ giá trị tối đa.

Kết quả là tổng giá trị lớn nhất mà tên trộm có thể mang đi được, được ghi vào tệp DRSEM.OUT.

One thought on “Giải bài toán xếp ba lô (bài toán cái túi) với C++

  1. DieuMo says:

    Tác giả viết code mà có test lại chưa . code TromDo ra kết quả 22. Code giữa bị lỗi, code dưới ra kết quả 15. Code này do tác giả viết hay copy bài từ trang khác vậy

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *