
网上有个平衡集合的问题,好像也是这么回事,只是它一开始就是一个数组,当然是偶数个数的元素。用的所谓的,回溯搜索法。从数组中取出一半的数,剩下的就是另一半的数。把所有的情况都找一遍,得到的差值最小的就是答案。是的这种相当于穷举的方法,在数不多的情况下还是蛮好用的。但是如果数很多的话,是无法完成的,时间复杂度已经超过指数级了。

当然这只是个近似算法,但是在数据个数较多,分配较均匀的情况下,可以很快的找出比较精确的结果:

/**********************************************用双端队列实现的近似算法******************************************************************/
#include<iostream>
#include<deque>
using namespace std;
deque<int> allnum;
void mysort(deque<int>& allnum,int a[],int lenth);
void display(int a[],int lenth);
int distribute(deque<int>& allnum,int a[],int b[]);
int main()
{
int a[]={176,781,3332,214,7685,11362,67,100,899,10000,32,114,5,112,67,100,211,221,421,215,212,67,100,1000,232,421,578,12,67,100};
int b[]={72,36673,3,23,22240,45,98,1990,777,211,112,334,215,12,67,100,143,212,214,521,612,67,100,1321,442,214,875,1112,67,100};
int n=30;
//int a[]={89,13,14,2,16};
//int b[]={42,62,21,20,38};
//int n=5;
mysort(allnum,a,n);
mysort(allnum,b,n);
cout<<"A数组:"<<endl;
display(a,n);
cout<<"B数组:"<<endl;
display(b,n);
cout<<"所有数平移后排序:"<<endl;
for(deque<int>::iterator iter=allnum.begin();iter!=allnum.end();++iter)
{
cout<<*iter<<' ';
}
cout<<endl;
cout<<"进行分配..."<<endl;
int dis=distribute(allnum,a,b);
cout<<"分配完成:"<<endl;
cout<<"A数组:"<<endl;
display(a,n);
cout<<"B数组:"<<endl;
display(b,n);
cout<<"两数组和数之差:"<<endl;
cout<<dis<<endl;
return 0;
}
void display(int a[],int lenth)
{
for(int i=0;i<lenth;i++)
{
cout<<a[i]<<' ';
}
cout<<endl;
}
int distribute(deque<int>& allnum,int a[],int b[])
{
int dis=0,index=0;
while(!allnum.empty())
{
int fpb=allnum.front()-allnum.back();//最大数与第二大数之差
int fpt=allnum.front()-allnum[1];//最大数与最小数之差
if(dis<0)//a中和数比b中小,a取最大数,b取较小数
{
if(abs(fpb+dis)<abs(fpt+dis))
{//dis为负数,用+dis表示差,用abs求绝对值
dis+=a[index]=allnum.front();
allnum.pop_front();
dis-=b[index]=allnum.back();
allnum.pop_back();
}
else
{
dis+=a[index]=allnum.front();
allnum.pop_front();
dis-=b[index]=allnum.front();
allnum.pop_front();
}
}
else//a中和数不比b中小,b取最大数,a取较小数
{
if((abs(fpb-dis)<abs(fpt-dis)))
{
dis-=b[index]=allnum.front();
allnum.pop_front();
dis+=a[index]=allnum.back();
allnum.pop_back();
}
else
{
dis-=b[index]=allnum.front();
allnum.pop_front();
dis+=a[index]=allnum.front();
allnum.pop_front();
}
}
index++;
}
return dis;
}
void mysort(deque<int>& allnum,int a[],int lenth)//创建顺序链表,从大到小的顺序
{
int i;
for(i=0;i<lenth;i++)
{
if(allnum.empty())
{
allnum.push_back(a[i]);
}
else
{
deque<int>::iterator iter=allnum.begin();
while(iter!=allnum.end())
{
if(*iter<a[i])
{
allnum.insert(iter,a[i]);
break;
}
++iter;
}
if(iter==allnum.end())
{
allnum.push_back(a[i]);
}
}
}
}
/**********************************************用双端队列实现的近似算法******************************************************************/
好吧,当然应该有其它更好的方法来实现。但是说回到这个问题上来,要是不是天梯分配,只是两个贼分赃或其它情况,为什么一定要数量相等呢?
这样就产生了一个新的问题:
一个数组分成两个数组,使两个新数组之和尽量接近。两个数组元素不一定要数量相同。
例如数组:int num[]={34,32,234,221,122,152,133,56,213,623};可以分成{34,56,213,234,221,152}和{32,122,623,133}.两组和都为910.
当这个新问题在脑海中产生的时候,觉得和那个问题也没多大区别。
就在一个晚上在图书馆看书时,突然想到了二叉树,又想到了哈夫曼,立刻关上书。拿出笔在纸上画了一下,很快就有了思路:

/*******************************************************************哈夫曼二叉树实现划分数组*********************************************************/
#include<iostream>
#include<deque>
using namespace std;
template<typename T>
class BinTreeNode//二叉树结点类的定义
{
typedef BinTreeNode<T>* nodepoint;
T data;//结点的数据域
nodepoint lchild;//左孩子指针
nodepoint rchild;//右孩子指针
nodepoint parent;//双亲指针
public:
BinTreeNode(){parent=NULL;lchild=NULL;rchild=NULL;}
BinTreeNode(T e)//通过元素值创建结点
:data(e),parent(NULL),lchild(NULL),rchild(NULL){}
T getdata(){return data;}
nodepoint getlchild(){return lchild;}
nodepoint getrchild(){return rchild;}
nodepoint getparent(){return parent;}
bool linklchild(nodepoint l)
{
if(lchild!=NULL)return false;
lchild=l;
l->parent=this;
return true;
}
bool linkrchild(nodepoint r)
{
if(rchild!=NULL)return false;
rchild=r;
r->parent=this;
return true;
}
};
int comparer(const void* number1, const void* number2)
{
return *(int*)number1 - *(int*)number2;//懒得自己写排序了,直接用qsort快排了
}
typedef BinTreeNode<int> mytree;
deque<int> one,two;// 两个队列用来存放最后结果
void display(mytree* tree,bool isone);//后序遍历二叉树,将结果保存到两个数组
int main()
{
deque<mytree*> pointlist;//二叉树结点指针队列
//int num[]={34,32,234,656,433,56,232,121,34,98,100,450,34,32,353,988,777,65,433,98,1111,983,645,232};//原数组
//int n=24;//数组长度
int num[]={34,32,234,221,122,152,133,56,213,623};
int n=10;
qsort(num,n,sizeof(int),comparer);//用快速排序从小到大排序num数组
int i;
cout<<"排序后的数组:"<<endl;
for(i=0;i<n;i++)
{
cout<<num[i]<<' ';
}
cout<<endl;
for(i=n-1;i>=0;i--)
{
mytree* temp=new mytree(num[i]);//为数组创建二叉树结点,将结点指针保存在队列中
pointlist.push_back(temp);//指针队列中是按从大到小排列的
}
while(pointlist.size()>1)//构建哈夫曼树,直到队列中只有一个结点,即为根结点
{
int dis=pointlist[0]->getdata()-pointlist[1]->getdata();//求最大两数的差
mytree* temp=new mytree(dis);//为差创建新结点
temp->linklchild(pointlist[0]);//最大数作为新结点的左孩子
temp->linkrchild(pointlist[1]);//第二大数作为新结点的右孩子
pointlist.pop_front();
pointlist.pop_front();//将两数出列
deque<mytree*>::iterator b=pointlist.begin(),e=pointlist.end();
for(;b!=e;++b)
{
if((*b)->getdata()<temp->getdata())
{
pointlist.insert(b,temp);
break;
}//在队列中找到比新结点小的元素,将新结点插入其前面。
}
if(b==e)pointlist.push_back(temp);//所有元素都比新结点大,将新结点插入队尾
}
mytree* root=pointlist[0];//将根结点取出
display(root,true);//将树上结点的元素分配到两数组中
int onesum=0,twosum=0;//用于计算两数组之和
cout<<"第一组数:"<<endl;
for(int k=0;k<one.size();k++)
{
onesum+=one[k];
cout<<one[k]<<' ';
}
cout<<endl;
cout<<"第一组数总和:"<<endl;
cout<<onesum<<endl;
cout<<"第二组数:"<<endl;
for(int j=0;j<two.size();j++)
{
twosum+=two[j];
cout<<two[j]<<' ';
}
cout<<endl;
cout<<"第二组数总和:"<<endl;
cout<<twosum<<endl;
cout<<"两组数总和之差:"<<endl;
cout<<root->getdata()<<endl;
return 0;
}
void display(mytree* tree,bool isone)
{
if(tree->getlchild()!=NULL)display(tree->getlchild(),isone);//左子树
if(tree->getrchild()!=NULL)display(tree->getrchild(),!isone);//右子树反转
if(tree->getlchild()==NULL&&tree->getrchild()==NULL)//该结点为叶子结点,没有子树
{
if(isone)one.push_back(tree->getdata());
else two.push_back(tree->getdata());
}
}
/*******************************************************************哈夫曼二叉树实现划分数组*********************************************************/
603

被折叠的 条评论
为什么被折叠?



