Hướng giải của Phân Công Công Nhân Kho


Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.

Tác giả: kienadmin

Lời giải: Phân công \(N\) người – \(N\) việc, tổng thời gian nhỏ nhất

Tư duy

Với mỗi cách giao việc, người \(0, 1, \ldots, N-1\) sẽ lần lượt nhận một việc chưa ai làm. Ta mô tả tập các việc đã giao qua một mask.

Thuật giải

Đặt dp[mask] = tổng thời gian nhỏ nhất khi đã giao các việc trong mask cho popcount(mask) người đầu tiên.

  • Khởi tạo: dp[0] = 0.
  • Chuyển trạng thái: với mask, người tiếp theo là p = popcount(mask). Ta thử giao việc j (chưa giao) cho người p, cập nhật dp[mask | (1<<j)] = min(dp[mask | (1<<j)], dp[mask] + c[p][j]).

Kết quả là dp[FULL] với FULL = (1<<N) - 1.

Đánh giá độ phức tạp

  • Thời gian: \(O(N \cdot 2^N)\).
  • Bộ nhớ: \(O(2^N)\).

Mã nguồn tham khảo

#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e15;

int main() {
    ios_base::sync_with_stdio(false); cin.tie(NULL);
    int n;
    if (!(cin >> n)) return 0;
    vector<vector<long long>> c(n, vector<long long>(n));
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) cin >> c[i][j];

    int full = (1 << n) - 1;
    vector<long long> dp(1 << n, INF);
    dp[0] = 0;
    for (int mask = 0; mask <= full; mask++) {
        int p = __builtin_popcount(mask);  // người tiếp theo được nhận việc
        if (p >= n) continue;
        for (int j = 0; j < n; j++) {
            if (mask & (1 << j)) continue;
            int nmask = mask | (1 << j);
            dp[nmask] = min(dp[nmask], dp[mask] + c[p][j]);
        }
    }
    cout << dp[full] << "\n";
    return 0;
}

Nhận xét

Không có ý kiến tại thời điểm này.