UVa 12911 Subset Sum

9 篇文章 1 订阅

本题无法通过动态规划予以解决。

由于本题中 N N N 最大为 40 40 40,生成所有子集显然不可行,需要适当变通。实际上,我们可以将数组分成两个数组 A A A B B B,其大小只相差 1 1 1,分别生成 A A A B B B 的所有子集和,由于 N N N 最大为 40 40 40,则 A A A 的大小最大为 20 20 20 A A A 中不同的子集和最多有 2 20 2^{20} 220 个,这是可行的。

对于数组 A A A 的每个子集和 s s s,在数组 B B B 的子集和中查找 T − s T - s Ts 是否存在,若存在,则将数组 A A A 中子集和 s s s 的数量 C s C_{s} Cs 乘以数组 B B B 中子集和 T − s T - s Ts 的数量 C T − s C_{T-s} CTs,这就是对总方案数的贡献,逐个处理数组 A A A 的所有子集和累加即可,算法的时间复杂度为 O ( N × 2 N / 2 ) O(N \times 2^{N/2}) O(N×2N/2)

可以使用 C++ \texttt{C++} C++ STL \texttt{STL} STL 中的映射( map \texttt{map} map)来存储子集和的个数。
需要注意,在解题过程中还需考虑子集不能为空。

// Subset Sum
// UVa ID: 12911
// Verdict: Accepted
// Submission Date: 2021-11-11
// UVa Run Time: 1.630s
//
// 版权所有(C)2021,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>

using namespace std;

int N, T, si[41];
map<long long, int> Ca, Cb;

int main(int argc, char *argv[])
{
    cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);

    while (cin >> N >> T)
    {
        for (int i = 0; i < N; i++) cin >> si[i];
        int Na = N / 2;
        if (Na == 0) Na++;
        int Nb = N - Na;
        Ca.clear();
        for (int i = 1; i < (1 << Na); i++)
        {
            long long sum = 0;
            for (int j = 0; j < Na; j++)
                if (i & (1 << j))
                    sum += si[j];
            Ca[sum]++;
        }
        Cb.clear();
        for (int i = 1; i < (1 << Nb); i++)
        {
            long long sum = 0;
            for (int j = 0; j < Nb; j++)
                if (i & (1 << j))
                    sum += si[Na + j];
            Cb[sum]++;
        }
        long long cnt = 0;
        for (auto p : Ca)
            if (Cb.count(T - p.first))
                cnt += 1LL * p.second * Cb[T - p.first];
        cout << cnt + Ca[T] + Cb[T] << '\n';
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值