Hướng dẫn giải của Xây dựng công ty

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 làm lời giải.


Nộp code mẫu trước khi tự giải được bài tập là một hành vi có thể bị ban.

Subtask ~1~ ~(1 \le n \le 20)~

  • Với giới hạn ~n~ nhỏ, ta có thể dùng kỹ thuật quay lui để sinh tất cả cách chọn trong thời gian chạy ~O(2^n)~.
Code tham khảo
#include <bits/stdc++.h>
using namespace std;
const int maxN = 25;

int n, k, a[maxN], cnt = 0;

void back_track(int idx, int sum) {
    if (idx > n) {
        cnt += (sum == k);
        return;
    }
    back_track(idx + 1, sum);
    back_track(idx + 1, sum + a[idx]);
}

int main() {
    cin >> n >> k;
    for (int i = i; i <= n; i++) {
        cin >> a[i];
    }
    back_track(1, 0);
    cout << cnt;
    return 0;
}

Subtask ~2~ ~(n \le 40)~

Cách ~1~
  • Ta áp dụng quay lui cùng với kỹ thuật chia đôi tập hợp để giảm giới hạn quay lui xuống với độ phức tạp ~O(2^{\frac{n}{2}} \times log_2(2^{\frac{n}{2}}))~
Code tham khảo của bachminhkhang :
#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 3;

int n, k, c[N];
long long res = 0;
vector<int> a, b;

void sol1(int pos,int val)
{
    if(val > k) return;
    if(pos == n / 2 + 1)
    {
        a.push_back(val);
        return;
    }
    sol1(pos + 1, val);
    sol1(pos + 1, val + c[pos]);
}

void sol2(int pos,int val)
{
    if(val > k) return;
    if(pos == n + 1)
    {
        b.push_back(val);
        return;
    }
    sol2(pos + 1, val);
    sol2(pos + 1, val + c[pos]);
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n  >> k;
    for(int i = 1; i <= n; ++i)  cin >> c[i];
    sol1(1, 0);
    sol2(n / 2 + 1, 0);
    sort(b.begin(),b.end());
    for(auto &u : a)
    {
        int l = lower_bound(b.begin(), b.end(), k - u) - b.begin();
        int r = upper_bound(b.begin(), b.end(), k - u) - b.begin();
        res +=  r -  l;
    }
    cout << res;
    return 0;
}
Cách ~2~
  • Vì giới hạn ~a_i~ nhỏ, ta có thể lập công thức quy hoạch động như sau:
    • Gọi ~dp[val]~ là số cách để tạo một team có tổng bằng ~val~, ~dp[0] = 1~.
    • Ta có công thức truy hồi: ~dp[k] = \sum_{i = 1}^{n} dp[val - a[i]]~ ~(~với ~val : k \rightarrow 1)~.
  • ĐPT: ~O(n \times k)~
Code tham khảo:
#include <bits/stdc++.h>
using namespace std;
const int maxN = 45;
const int maxK = 1e6 + 5;

int n, k, a[maxN], dp[maxK];

int main() {
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    dp[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int val = k; val > 0; val--) {
            if (val - a[i] >= 0) {
                dp[val] += dp[val - a[i]];
            }
        }
    }
    cout << dp[k];
    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


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