如何将一个整数数组分成两个子数组,使它们的平均数相等?

7
请帮我解决上述问题。提供一个工作示例的算法将不胜感激。如果无法实现上述要求,请处理相关情况。
目前我得到的是:遍历整个数组并获取整个数组的平均值,时间复杂度为O(n)。我们称之为avg。然后从这个平均值中可以得出除了a[0]以外整个数组的平均值(公式为:avg(a[1..n-1])=(avg(a[0..n-1])*n-a[0])/(n-1))。接下来,你需要拿这个新的平均值和a[0]进行比较。如果它们不相等,你就继续计算先前知道的平均值,并使用上述公式进行计算。
虽然有人已经提供了一个“可行”的解决方案,但我正在寻找最有效的实现方式。

11
请发布你目前为止编写的代码。通常人们不喜欢为你编写代码。现在这只是一个工作描述,而不是一个问题。 - Mitch Wheat
2
@Mitch:我已经给出了我的答案。现在你能给出你的吗? - Programmer
您提出的解决方案总体上是O(n),我认为它不能比这更有效率,因为您必须查看数组的每个元素一次。 - Björn Pollex
这不是一个分区问题吗?原始列表中的数字可以在list1中,也可以不在(如果不在,则在list2中)。 - Satadru Biswas
1
你必须将数组分成左/右两部分,还是可以自由地重新排列元素?例如,如果你的数组是[1;1;2;2],那么[1;2],[1;2]是否有效,或者被认为是不可行的? - Mathias
6个回答

2
一次快速的谷歌搜索返回了this。无论如何,如果您不需要高性能,回溯算法始终是一个选项。
编辑: 我试图使用回溯算法。我的解决方案绝不高效。当然,您可以替换平均方法以避免另一层循环。此外,为了显示没有解决方案,您只需计算解决方案的数量。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

public class SOQuestion {

    /**
     * just prints a solution
     * 
     * @param list
     * @param indexes
     */
    public static void printSolution(List list, HashSet indexes) {
        Iterator iter = indexes.iterator();
        while (iter.hasNext()) {
            System.out.print(list.get((Integer) iter.next()) + " ");
        }
        System.out.println();
    }

    /**
     * calculates the average of a list, but only taking into account the values
     * of at the given indexes
     * 
     * @param list
     * @param indexes
     * @return
     */
    public static float avg(List list, HashSet indexes) {
        Iterator iter = indexes.iterator();
        float sum = 0;
        while (iter.hasNext()) {
            sum += (Integer) list.get((Integer) iter.next());
        }
        return sum / indexes.size();
    }

    /**
     * calculates the average of a list, ignoring the values of at the given
     * indexes
     * 
     * @param list
     * @param indexes
     * @return
     */
    public static float avg_e(List list, HashSet indexes) {
        float sum = 0;
        for (int i = 0; i < list.size(); i++) {
            if (!indexes.contains(i)) {
                sum += (Integer) list.get(i);
            }
        }
        return sum / (list.size() - indexes.size());
    }

    public static void backtrack(List list, int start, HashSet indexes) {
        for (int i = start; i < list.size(); i++) {
            indexes.add(i);
            if (avg(list, indexes) == avg_e(list, indexes)) {
                System.out.println("Solution found!");
                printSolution(list, indexes);
            }
            backtrack(list, i + 1, indexes);
            indexes.remove(i);
        }
    }

    public static void main(String[] args) {
        List test = new ArrayList();
        test.add(2);
        test.add(1);
        test.add(3);

        backtrack(test, 0, new HashSet());
    }
}

我检查过了,它们不准确。其中大部分都是错误的。 - Programmer
3
@drstupid:一个好的答案应该是自包含和完整的(如果可能的话)。如果你只有一个你谷歌过的链接,那就把它作为评论。对于单个关键词(回溯),也是如此。要将其作为答案,解释如何解决问题,并将这些链接作为参考资料发布。 - Björn Pollex
@Space_C0wb0y 给我一些时间来开发答案会更好。但无论如何,还是谢谢你的提示。我也认为在给出完整解决方案之前先给出快速指针对于提问者会有所帮助。我的错。 - drstupid
你能给我一个高效的解决方案吗?非常感谢你的努力。 - Programmer
5
@程序员,我认为我已经给了你足够的指引来改进解决方案。如果你在寻找有人替你完成工作,那么SO并不是合适的地方。请注意不要改变原意,让翻译更通俗易懂。 - drstupid

0

尝试这段代码...

#include <QList>
#include <QDebug>

/****

We sort the array in the desending order. So the first two elemnts
are the biggest element of the array. These are put in split1 and the
split2 array. Note that we start from the biggest element the average
is going to be much higher than the final avarage. Next we start placing
the smaller elemnts from source to split1 or split2. To do this we make a
simple dicision. We look at the value to be added and then see if that will
increase/decrease the average of a given split. Accordingly we make our
decesion.

****/


static bool averageSplit(const QList<int>& source,
                         QList<int>& split1,
                         QList<int>& split2)
{
    if (source.size() < 2) {
        return false;
    }

    QList<int> list = source;
    qSort(list.begin(), list.end(), qGreater<int>());

    double sum = 0;
    foreach(int elm, list) {
        sum += elm;
    }
    const double totalAvg = sum / list.size();

    double sum1 = list[0];
    double sum2 = list[1];
    split1.append(list[0]);
    split2.append(list[1]);
    for(int i = list.size() - 1; i >= 2; --i) {
        double avg1 = sum1 / split1.size();
        double avg2 = sum2 / split2.size();
        int val = list[i];
        if (avg1 < avg2) {
            // Try to increase tha avg1 or decrease avg2.
            if (val > split1.size()) {
                split1.append(val);
                sum1 += val;
            }
            else {
                split2.append(val);
                sum2 += val;
            }
        }
        else {
            // Try to increase tha avg2 or decrease avg1.
            if (val > split2.size()) {
                split2.append(val);
                sum2 += val;
            }
            else {
                split1.append(val);
                sum1 += val;
            }
        }
    }

    qDebug() << "totalAvg " << totalAvg;
    qDebug() << "Avg1 " << sum1 / split1.size();
    qDebug() << "Avg2 " << sum2 / split2.size();
}

void testAverageSplit()
{
    QList<int> list;

    list << 100 << 20 << 13 << 12 << 12 << 10 << 9 << 8 << 6 << 3 << 2 << 0;
    list << -1 << -1 << -4 << -100;

    QList<int> split1;
    QList<int> split2;
    averageSplit(list, split1, split2);

    qDebug() << "split1" << split1;
    qDebug() << "split2" << split2;
}

0
#include<stdio.h>

void partitionEqualAvt(int *a, int n)
{

    int i;
    int sum=0, sSum=0, eSum, sAvg, eAvg;
    for(i=0; i<n; i++)
        sum+=a[i];
    eSum=sum;
    for(i=0; i<n-1; i++)
    {
        sAvg=0;
        eAvg=0;
        sSum+=a[i];
        eSum=sum-sSum;
        sAvg=sSum/(i+1);
        eAvg=eSum/(n-i-1);
        if(sAvg == eAvg)
        {
            printf("\nAverage = %d from [%d to %d] and [%d to %d]", sAvg, 0, i, i+1, n);
        }
    }
}

int main()
{

    int a[]={1,1,1,1,1,1};
    int n=sizeof(a)/sizeof(int);
    partitionEqualAvt(a,n);
    return 0;
}

1
我们可以调整它以包括浮点平均值。 - Sunil Yaduvanshi

0

使用贪心算法 解决问题的一种方法是模仿孩子选择游戏队伍的方式,即使用贪心算法。该算法按降序迭代数字,将每个数字分配给较小总和的子集。当集合中的数字与其基数大小相当或更小时,此方法效果良好。

在Python中尝试这样做: 如果平均值存在,则可能找到它,否则它会让你接近平均值。

    a = [500, 1, -45, 46, -20, 100, -30, 4, 110]

    b, c = [], []

    m1, m2, n1, n2 = 0, 0, 0, 0
    count = 0
    for i in a:
        if count == 0:
            b.append(i)
            n1 += 1
            m1 = i
            n1 = 1
            count = 1
            continue
        else:
            tmp_m1 = (m1 * n1 + i) / (n1 + 1)
            tmp_m2 = (m2 * n2 + i) / (n2 + 1)
            print b, c, tmp_m1, tmp_m2
            if m1 > m2:
                if(tmp_m1 - m2 < m1 - tmp_m2):
                    b.append(i)
                    m1 = tmp_m1
                    n1 += 1
                else:
                    c.append(i)
                    m2 = tmp_m2
                    n2 += 1
            else:
                if(tmp_m1 - m2 < m1 - tmp_m2):
                    c.append(i)
                    m2 = tmp_m2
                    n2 += 1
                else:
                    c.append(i)
                    m1 = tmp_m1
                    n1 += 1
    print b, c, m1, m2

Sample output:
[500] [] 250 1
[500, 1] [] 151 -45
[500, 1, -45] [] 124 46
[500, 1, -45] [46] 108 13
[500, 1, -45, -20] [46] 106 73
[500, 1, -45, -20] [46, 100] 80 38
[500, 1, -45, -20, -30] [46, 100] 67 50
[500, 1, -45, -20, -30, 4] [46, 100] 73 85
[500, 1, -45, -20, -30, 4] [46, 100, 110] 73 73

0

这是我是如何做到的:
1 - 总数组 --> 如果是奇数,则无法创建两个单独的数组。O(n)
2 - 将总数除以2。
3 - 对数组进行排序(从高到低)--> O(NLogN)
4 - 从最高的数字开始,不断尝试通过转移到下一个最高的数字来获得剩余的金额。如果无法完成,则将该数字移动到另一个数组中。--> 我认为最坏情况是O(N^2)。

所以让我们以这个例子为例: 3, 19, 7, 4, 6, 40, 25, 8

  1. 总数 = 112
  2. 除以2 = 56
  3. 排序数组 - 3、4、6、7、8、19、25、40
  4. --> 列表1中有40(列表1总计=40,剩余16)
    --> 25太大了,在列表2中(列表2总计=25)
    --> 19太大了,在列表2中(列表2总计=44)
    --> 在列表1中测试8(列表1总计=48,剩余8)
    --> 在列表1中测试7(列表1总计=55,剩余1)
    --> 无法再添加1个
    --> 7未通过测试6(列表1总计=54,剩余2)
    --> 无法再添加2个
    --> 4未通过测试3
    --> 3回到8
    --> 将8移动到列表2并开始测试7(列表1总计=47,剩余9,列表2总计=52)
    --> 测试6(列表1总计=53,剩余3)
    --> 添加3个元素(列表1总计=56,成功)
所以这两个列表是[40,7,6,3]和[25,19,8,4]。

如果总数组长度为奇数,为什么不能创建2个数组?并没有说明生成的数组应该具有相同的长度。平均值必须相同,这可以通过[2,3,4,5,6] -> [2,4,6] + [3,5]来实现。 - drstupid
@richs的解释很好,附有示例。你所做的基本上是展示了如何使用回溯法来解决分割问题。然而,在最坏情况下,时间复杂度为O(2^n)。 - Nishant Kelkar

0

我认为如果原始数组中所有元素的总和是奇数,则无法将其分成两个数组。 但是,如果总和等于偶数,则可以开始制作您的数组,使得每个元素的总和等于SUM/2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接