用一些S计数大小为k的唯一子集

Count unique subsets of size k with some S

本文关键字:小为 唯一 子集      更新时间:2023-10-16

七队面对一个可怕的敌人。他只有在特殊情况下才能被击败力量的四重组合攻击(1<=S<=10^9)。鸣人、佐助、小樱和卡卡西必须同时攻击执行组合。它们中的每一个可以从N(1<=N<=1000)个攻击中进行选择,每个攻击具有强度si(0<=i<N,1<=si<=10^9)。个人攻击的力量加起来形成组合的强度。

他们可以使用有效的组合吗?请注意所有人都可以使用攻击。

您需要编写一个函数,该函数接受如下输入——整数N作为攻击次数,整数向量s[]作为N攻击的强度和整数S作为所需强度组合的。将输出变量设置为不同的有效数量combo。

如果两种组合的强度相差至少使用了一次攻击。

输入:1 {1} 4

输出:1 ===>{1,1,1,1}

输入:2 {1,2} 5

输出:1 ===> {1,1,1,2}

下面是我的代码,它只通过了10个测试用例中的3个。我不知道测试用例,因为它是一些在线代码提交。

我的算法:1) 创建一个散列,其中索引是输入数组中成对的总和,值是对总和有贡献的单个元素2) 在散列上迭代,看看散列中的i是否有k-i3) 计数以上索引和返回计数/2,因为我们正在对H(i)和H(k-i)进行计数

请查看代码并告诉我你认为代码不会产生正确的o/p的场景。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<map>
#include<set>
const int noOfPalyers = 4;
int validCombo(int input1,int input2[],int input3)
{
    //Write code here
    int count = 0;
    std::vector<int> vec;
    int size =input1*noOfPalyers;
    for(int i = 0; i < input1; i++)
    {
        for(int j = 0; j < noOfPalyers;j++)
        {
            vec.push_back(input2[i]);
        }
    }
    std::vector< std::set< std::pair<int, int> > > vecHash;
    //vecHash.reserve(size*size);
    for(int i =0; i < (size*size); i++)
    {
        vecHash.push_back(std::set< std::pair<int, int> >());
    }
    for(int i =0; i < size; i++)
    {
        for(int j =1; j < size; j++)
        {
            int key = vec[i] + vec[j];
            if(vec[i]<= vec[j])
                vecHash[key].insert(std::make_pair(vec[i], vec[j]));
            else
                vecHash[key].insert(std::make_pair(vec[j], vec[i]));
        }
    }
    for(int i = 0; i < input3; i++)
    {
        if(vecHash[i].size() > 0 &&  i < input3)
        {
            if(vecHash[input3-i].size() > 0)
            {
                std::set< std::pair<int, int> >::iterator iter, iter2;
                for(iter=vecHash[i].begin(); iter!=vecHash[i].end();++iter)
                {
                    for(iter2=vecHash[input3-i].begin(); iter2!=vecHash[input3-i].end();++iter2)
                    {
                        std::cout<<(*iter).first<<","<< (*iter).second<<",";
                        std::cout<<(*iter2).first<<","<< (*iter2).second;
                        std::cout<<"n";
                        count++;
                    }
                }
            }
        }
    }
    return (count ==1 ? count: count/2);
}
int  main()
{
    int i = 3;
    int arr[] = {1,2,3};
    int j = 7;
    int arr1[] ={1};
    std::cout <<"o/p == "  << validCombo(i, arr, j)<< "n";
    std::cout <<"o/p == "  << validCombo(1, arr1, 4);

    //getch();
    return 0;
}

UPD。我的上帝,现在我已经理解了你的评论:)并看到你试图用我写的方式解决它。不管怎样,我希望你会发现我的一些解释很有用。首先,您不使用散列函数(我知道身份函数是散列函数,但在我们的情况下不是好的)
我也不理解你的count逻辑。。。我认为您需要再次阅读Two combinations are different if they differ in strength of at least one attack used.部分,并检查您的count逻辑。

这只是我的第一个想法。希望能有所帮助。

===================================

你知道,这个问题是关于集合中4个数字的特定和。让我们想象一下,我们只有两个英雄(所以我们的总和中有两个术语):

a + b = S,

其中,a、b是来自N个数字集的攻击强度(我们将其命名为T)。不同的a + b和的数目是N^2。简单地计算所有这些和,然后搜索那些等于S的和,并不能给我们很好的解决方案。这个问题可以用更好的复杂性来解决。

如果我们能找到一个快速函数,这样:

F(a) = F(S - b)

我们将预先计算所有的F(S-b),然后在所有的a上循环,并找到哪些满足上面的等式。你提到了哈希。哈希函数可以做到这一点。我们需要这样的散列函数来映射从集合T到范围[0,N]的所有数字。因为我们只有不超过N个不同的a
但我们有一个小问题:

  • CCD_ 16只是意味着CCD_ 17可以等于CCD_。幸运的是,这不是什么大问题
  • 因为主功率是:CCD_ 19表示CCD_

好的,正如你所看到的,我们有算法来解决具有摊销O(N)复杂性的a + b = S问题。听起来不错,很有希望,对吧?:)

===================================

现在回到你的问题:

a + b + c + d = S

我的想法是用O(N^2)预先计算所有f(a + b),并像这样存储它们(警告!只是伪代码):

vector<int> hash = new vector<int>(with size N)
foreach (a in T)
foreach (b in T)
{
    int x = OurHashFunc(a + b);  // a + b <= 2 * 10^9 so it never overflows int
    if (hash[x] == null)
        hash[x] = new vector<pair<int,int>>
    hash[x].push_back(new pair<int, int>(a, b));
}

我们保持对(a,b)能够恢复初始和的项。然后,如果我们的OurHashFunc是加性的,那么将我们的初始问题转换为这样:

a + b + c + d = S             // apply hash func => 
f(a + b) + f(c + d) = f(S)    // rename => 
x + y = Z                     // wow, I bet I've already seen this equation ;)

现在,4项和问题已经简化为2项和问题,开销为O(N^2)。我认为这种减少可以继续:2^k项和问题应该有avg O(N^k)的解。