当前位置: 首页 > 知识库问答 >
问题:

拔河:将n个对象的集合int划分为子集

栾昂雄
2023-03-14

声明

给定一个n个整数的集合,将集合分成两个大小为n/2的子集,使两个子集之和的差值尽可能小。若n为偶数,则两个子集的大小必须严格为n/2,若n为奇数,则一个子集的大小必须为(n-1)/2,另一个子集的大小必须为(n+1)/2。

例如,设给定集为

{3, 4, 5, -3, 100, 1, 89, 54, 23, 20}
{4, 100, 1, 23, 20} 
{3, 5, -3, 89, 54}
{23, 45, -34, 12, 0, 98, -99, 4, 189, -1, 4}

输出子集应为

{45, -34, 12, 98, -1} 

而且

{23, 0, -99, 4, 189, 4}

两个子集的元素和分别为120和121。

for (int i = 1; i <= N; ++i)
    for (int j = sum; j >= 0; --j)
        if (dp[j])
            dp[j + W[i]] |= dp[j] << 1;

共有1个答案

范宏大
2023-03-14

我觉得这句话说得很清楚。

 // If (dp[i] << j) & 1 is 1, that means it is possible
 // to select j out of the N people so that the sum of
 // their weight is i.

从初始条件dp[0]=1<<0开始,意思是“可以选择0个人,使他们的权重之和为0”。

然后,对于dp中非零的每个条目(即if(dp[j])部分),更新列表中当前人员的dp。

请注意,这个算法不会告诉您需要选择哪些数字才能得到这个和;信息丢失了。它只会告诉你最好的两个和是什么。尽管稍微修改算法并使用某种数据结构来保留这些信息并不难(例如dp中的每个条目都说“我可以从3个数字中求出这个和,这些数字是……”)。

哦,还有关于向后迭代的问题:这是为了防止我们对同一个数字进行两次计数。如果第一个条目是1,我们会说“I can make 0 from 0 numbers;now I can make 1 from 1 numbers”。紧接着,“我可以从1个数字变成1;现在我可以从2个数字变成2”。等等。

编辑:既然你问了,这里有一种方法(请注意,如果你输入非正数,它就会坏掉,我就让你来解决这个问题):

int N;
int W[100 + 5];
std::map<int, std::vector<int>> dp[450 * 100 + 5];

void solve()
{
    int sum = accumulate(W + 1, W + N + 1, 0);

    // If (dp[i][j]) contains a nonempty vector, that means it is possible
    // to select j out of the N people so that the sum of
    // their weight is i, with those people's indices being the values of said vector
    dp[W[1]][1].push_back(1);

    for (int i = 2; i <= N; ++i)
    {
        for (int j = sum; j >= 0; --j)
        {
            for (std::map<int, std::vector<int>>::iterator it = dp[j].begin(); it != dp[j].end(); ++it)
            {
                dp[j + W[i]][it->first+1] = it->second;
                dp[j + W[i]][it->first+1].push_back(i);
            }
        }
    }

    std::vector<int> selected;
    int minDiff = 450 * 100;
    int teamOneWeight = 0, teamTwoWeight = 0;
    for (int i = 0; i <= sum; ++i)
    {
        if (!dp[i].empty())
        {
            int diff = abs(i - (sum - i));
            if (diff < minDiff)
            {
                minDiff = diff;
                teamOneWeight = i;
                teamTwoWeight = sum-i;
                selected = dp[i].begin()->second;
            }
        }
    }
    cout << "Team 1, sum of " << teamOneWeight << ": ";
    for (int i = 1; i <= N; ++i)
    {
        if (std::find(selected.begin(), selected.end(), i) != selected.end())
            cout << W[i] << ' ';
    }
    cout << endl << "Team 1, sum of " << teamTwoWeight << ": ";
    for (int i = 1; i <= N; ++i)
    {
        if (std::find(selected.begin(), selected.end(), i) == selected.end())
            cout << W[i] << ' ';
    }
    cout << endl;
}
 类似资料:
  • 如何将整数数组划分为N个子集,使这些子集的和最小? 例如,数组由11个元素组成,我需要其中的6个子集。 子集:<code>{2,1,1,3},{4},}4,3},}3,2},1,2},{3}</code>最小和=7。 另一个答案是:最小和=7。 注意:在分区时,必须保持数字在原始集合中出现的顺序。

  • 我想在到期日对一个Retrient对象集合进行分组,但我想为每个组创建一个新的RentalReport对象,其中键是预定义的值(枚举),并且组是该对象的属性。我通过根据每个条件拟合集合并为每个条件创建一个RentalReport对象来实现这一点,但我想知道是否可以使用Collectors类的groupingBy方法来实现这一点。 有没有可能在Java 8中按一组预定义的过滤器进行分组,这样我就可以

  • 我尝试使用Java8Lambda表达式和流来解析一些日志。我有一个巨大的日志文件,运行了一次又一次。我想把它分成不同的集合,每次运行一个集合。我不知道日志在advanced中运行了多少次。为了锻炼我非常虚弱的lambda肌肉,我想在列表中一次完成。 这是我目前的实现: 这里基本上类似于TomekRekawek的解决方案,但首先是未知的分区大小。

  • 现有两个名为getDetails(…)的方法 。一个需要至少一个必需参数,另一个需要一个集合(不验证集合的内容/大小)。 问题是集合有时会作为空传递,并且根据我的业务案例,我总是期望至少传递一个值。因此,我需要将该方法设为私有,它接受集合。 我计划将下面的方法作用域设置为private,这样调用者就不会使用零属性调用此方法。 调用方方法之一是将集合对象传递给下面的getDetails, id.ge

  • 我一直陷在这个问题中,找不到有效的解决办法。 我有N(高达1000万)说最大100个元素的数组。这些数组包含1-10000的数字。 现在我的问题是将这些数组划分为K个组,这样我就可以最小化所有数组中的重复项,即一个数组包含1,4,10,100,另一个数组包含1100。我希望他们进入同一组,因为这样可以最大限度地减少口是心非。我的问题的两个限制条件如下- > 组中向量的数量应均匀分布。 根据大小以递

  • 问题内容: 我对Apache Spark和Python比较陌生,想知道像我将要描述的东西是否可行? 我有一个格式为[m 1,m 2,m 3,m 4,m 5,m 6, … m n ]的RDD(运行rdd.collect()时会得到这个)。我想知道是否有可能将此RDD转换为[[m 1,m 2,m 3),(m 4,m 5,m 6).....(m n-2, m n-1,m n)]。内部元组的大小应为k。如