数字数组平分算法 跪求~!

小落勇士 2007-05-25 06:19:48
假设我有 1 4 5 21 8 11 一个N(偶数)的数字序列,如何尽量将他们分为两组,使他们之和尽量平衡!!
算法??
...全文
1017 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
wangwang1103 2007-07-21
  • 打赏
  • 举报
回复
mark
ayw215 2007-06-19
  • 打赏
  • 举报
回复
学习
DLMU_net 2007-06-19
  • 打赏
  • 举报
回复
Vitin(卫亭) 分析的很精彩,支持,Vitin以前是学数学的吧?
jinwei1984 2007-06-19
  • 打赏
  • 举报
回复
mark!
lixivip1 2007-06-19
  • 打赏
  • 举报
回复
可以这样算,当然仅仅对于本题来说。把数组前半部分后半部分分成2个数组,然后用第2个数组中的第一个元素分别替换第1个数组中的3个元素,算2个数组的差是否变大,变大则,换回来,不变则保留,若为0则就是最佳答案,依次类推,最多比较(n/2)^2次就可以得到结果
xuejinn 2007-06-17
  • 打赏
  • 举报
回复
mark
topher2 2007-06-17
  • 打赏
  • 举报
回复
mark
Aretobo12 2007-05-29
  • 打赏
  • 举报
回复
(又来了,最近比较闲:)回头看了看自己写的东西,发现一个思考不成熟的地方,不好意思,为了不误导大家,我修正一下:
-:我说的类似平衡二叉的算法有个Bug,在很多情况下是不能工作的,尤其是输入数据是从小到大
排好序的情况。
改正:在执行这个算法前对输入数据进行一次快排,不过是从大到小。然后进行树的构建,这样就对了。
-:又想了一下,其实这样的算法时间复杂度和空间复杂度都不理想,其实是我SB了,这个题目其实可以很简单在O(N)的时间内实现,只要对数组进行一趟扫描就可以了,但是之前还是需要对数组进行排序(从小到大)。我简单描述一下思想:
1:定义两个集合(随便你采用什么数据结构实现) LeftSet = {}, RightSet = {}
2: 定义两个变量用作计数:LeftSum = RightSum 0
程序就一个循环
i = 0;
while (i < n)
{
LeftSum += a[i];
if (LeftSum <= RightSum)
i++;
else
{
while(LeftSum > RightSum)
{
// Pop the min member from LeftSet to RightSet
// RightSum += Poped
}
i++;
}

简单说一下,就是含大数的RightSum慢速增长,每次出现LeftSum > RightSum 把LeftSet中
最小的元素加到RightSet中。
可惜还是需要排序,不过空间复杂性下降了。(完)
Aretobo12 2007-05-28
  • 打赏
  • 举报
回复
提供大家一个思路,现在在上班没有时间给大家写程序了。这就是一个平衡二叉树思想的
编钟,逐个读入数组元素然后按规则加入到树中,最后左右子树构成的集合就是你想要的了。
如何构建这个数:
1:每个树节点包含如下的信息:
-:左右子树指针 (left, right)
-:该节点自己的数值(var)
-:以该节点为根的子树的数值之和 (sum)
2:加入节点p(递归)
-: 根的 sum += p->var
-: if left.sum > right.sum
递归 add_right_child
else
递归 add_left_child
-: 递归出口
medie2005 2007-05-26
  • 打赏
  • 举报
回复
背包问题。
dsniff 2007-05-26
  • 打赏
  • 举报
回复
昨天写了个描述,要回复的时候断网了,今天来就这么多了……
mark,明天再看
expter 2007-05-26
  • 打赏
  • 举报
回复
LS?
Vitin 2007-05-26
  • 打赏
  • 举报
回复
恩,这个问题的一般化就是一个背包问题。既是说,它的复杂度一定是2^N (除非数学有重大发展,呵呵)。
所以,对于这个问题,使用枚举法吧。不会有比它更快的算法了。

caithy2006提供的答案有一个前提条件,就是两组数目必须相等。可能这就是楼主需要的前提。那么在这个前提下,复杂度会下降,因为即使用枚举,从N中取N/2的复杂度也只有 N!/((N/2)!)^2 ,小于2^N 。
caithy2006算法的复杂度是N^2,但是有个错误,我先没仔细看,只是运行了一遍,结果是
11 22 3
其中3是group[2]原有的值,而不是数组var[6]中的,所以这其中有问题吧。
后来看了一下代码,group中应该存三个已有的值,改成
int group[3];
for (int i = 0; i < 3; i++)
group[i] = var[i];
这样如果var做了修改,group也自动修改好了。现在的结果是
11 22 10
对本测试案例来说确实是正确的。当然不能就此认为它在所有情况下都是正确的。
代码确实是完全按照caithy2006提出的基本思想编写的。只要这个思路是正确的,那么它就是正确的了。但是这个思路是正确的吗?可能是,也可能不是,需要严格的数学证明。

仍然与枚举做比较。这里的算法之所以复杂度小,是因为没有遍历到枚举的所有情况,其核心是每次只替换一个以及不回溯。那么
1)会否出现需要替换两个的情况呢?当然在半组数目为3(N=6)的情况没有这种可能性(比如组内的{a,b,c}及组外的{d,e,f},d,e大大远离平均值,没可能被选中,但是可能{d,e,c}{a,b,f}更接近。此时会将c移出,f移入,形成 {a,b,f}{d,e,c},恰恰满足条件。因为1+2=3,可以互补,所以在半组数目为3情况下,没有同时移动两个的可能),但是当N=8,10等等的情况呢?有可能就需要同时替换两个了。所以,只替换1个,这个1可能是(N/2)/2,那么再此思想中,N^2的复杂度就不能保证了。
2)已经移出的是否需要再度移入呢?例如,开始是{a,b,c},中间达到了{a,c,e},其实最佳答案是{a,b,f}。同样道理,在N=6的情况下这种可能性不太可能存在不存在,因为一共只有3个数字,出现这种情况至少和本组的其他两个数字都相关(注意,和上一点不同,我这里只是猜测不太可能,而非已经肯定了,这个难度比上一点大一些,所以没有花工夫去严格证明,只是指出这种可能性)。但是,同样当N增大时,组内元素的相关度就减少了,随意性也就加大了,有可能出现移出后再度移入的情况。
我想如果要证明这个思想的正确性,上述两点时需要考虑的。但无论如何,这个交换的基本思想时精彩的创新,也许只要对它稍加改动或补充,就可以完美地解决目前这个问题。又或者,它会对最终解决背包问题提供一个启示。所以,不必为方案地不完美而沮丧,只要你再原先不知道地情况下想出一种新方案(而不必在意是否有人先你一步),那么你就应该为自己的创新而感到自豪!
caithy2006 2007-05-25
  • 打赏
  • 举报
回复
/*已经在电脑运行过了*/
/* 基本思想是先把前3个数分在一个组,然后把剩下的3个值依次向这个组分,
再从这组里踢出一个能让剩下的三个数的和最接近所有值的和的一半
*/
#include <iostream>
#include<cmath>
using namespace std;
int main()
{ int var[6]={ 1, 2 ,10 ,11 ,22 ,42};
int group[3]={1,2,3};
int average2=0,trygroup=0,i=0;
for(;i<6;i++) average2+=var[i];
average2/=2;
for(i=0;i<3;i++) trygroup+=var[i];
for(int j=3;j<6;j++)
{ int m=-1,tem=trygroup+var[j]-average2;
int tem1=abs(trygroup-average2);
for(int k=0,tem2;k<3;k++)
{ tem2=abs(tem-group[k]);
if( tem1 > tem2) {m=k;tem1=tem2;}
}
if(m >= 0) {trygroup+=var[j]-group[m];group[m]=var[j];}
}
cout<< group[0]<<" " <<group[1]<<" "<<group[2];
cin>>i;/*看screen上的结果*/
return 0;
}
caithy2006 2007-05-25
  • 打赏
  • 举报
回复
/* 基本思想是先把前3个数分在一个组,然后把剩下的3个值依次向这个组分,
再从这组里踢出一个能让剩下的三个数的和最接近所有值的和的一半
*/
#include <iostream>
#include<cmath>
using namespace std;
int main()
{ int var[6]={ 1, 2 ,3 ,4 ,5 ,6};
int group[3]={1,2,3};
int average2=0,trygroup=0,i=0;
for(;i<6;i++) average2+=var[i];
average2/=2;
for(i=0;i<3;i++) trygroup+=var[i];
for(int j=3;j<6;j++)
{ int m=-1,tem=trygroup+var[j]-average2;
int tem1=abs(trygroup-average2);
for(int k=0,tem2;k<3;k++)
{ tem2=abs(tem-group[k]);
if( tem1 > tem2) {m=k;tem1=tem2;}
}
if(m >= 0) {trygroup+=var[j]-group[m];group[m]=var[j];}
}
printf("%d\n%d\n%d ",group[0],group[1],group[2]);
cin>>i;/*看screen上的结果*/
return 0;
}
sss_free 2007-05-25
  • 打赏
  • 举报
回复
求任意一对数的方差,方差说明了个数值接近的程度
littleBuggy 2007-05-25
  • 打赏
  • 举报
回复
分成两组后,两组的数字个数要相同吗?
loongee 2007-05-25
  • 打赏
  • 举报
回复
找平均数,然后大于平均数的一个组,小于平均数的一个组。如果要求两个数组的个数要一样,就排序,然后从两端开始取数,最大和最小的在第一个组,倒数第二大和倒数第二小的在第二个组,依此类推。
vcPlayer 2007-05-25
  • 打赏
  • 举报
回复
实则为一组合算法,只不过在组合上又多了两个小问题:

找出N/2个数,这些数之和 与 (N个数之和)/2 的差值最小即可。

小落勇士 2007-05-25
  • 打赏
  • 举报
回复
不对啊,
如果是 1 2 10 11 22 42
最佳组合为,不是从两头取
(42 2 1) (10 11 22)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧