有人提议说模拟 背包算法....背包算法大概可以表示为给你一个包,然后你让这个包尽可能的有价值,对应的就是,这个包的大小就是 sum(c)/2 (这样就可以让他们的绝对值最小),然后问题来了,这个算法只会视价值来分配,不会执着于时候分成两半........但是,他的解决思维还是可以借鉴的:
背包算法说,我在拿第 i 件的时候,分成两个情况,一种是不拿,一种是拿.
设 dp(i,j,k) 为,从前i件中拿j个数,且不能超过c 的最大值:
这样的话 递归方程 dp(i,j,k) = max( dp(i-1,j-1,k - c[i]) +c[i] , dp(i-1,j,c) );
用 node 链表来存储,分出来的结点索引。 - -|| 然后想来想去,这种方法真心复杂,而且复杂度为O(2^n)方,而且,分完以后...还有再通过那些序列来分数组。琢磨这种方法,主要是想,到底怎么样才可以存储到那些节点。有更好的方法,就提出来参考参考。
class node{ public: int index; node * next; node(int i):index(i),next(NULL){} }; int iSelectj(int i,int j,int c,node * &p){ //先判断是否超出了范围,或者数目不够了 if( c < 0 || i < j) return 0x80000000; //判断是否已经选够数字了 if( j == 0 ) return 0; //判断是否不合法 if(i < 1 ) return 0x80000000; //如果选择这个数的话,就生成这个结点 node * p1 = new node(i-1); int max1 = a[i-1]+iSelectj(i-1,j-1,c-a[i-1],p1); //不用的话,就继续用上一层传进来的结点 int max2 = iSelectj(i-1,j,c,p); cout << "iSelectj("<<i<<","<<j<<","<<c<<"):"; if( max1 > max2){ cout << "use "<< a[i - 1]<< "+iSelectj("<<i-1<<","<<j-1<<","<<c-a[i-1]<<")" << endl; //如果 max1 > max2 说明用了这个结点,那么max2后面采用的结点都不要 deleteNode(p->next); p->next = p1; return max1; }else{ cout << "use " << "iSelectj("<<i-1<<","<<j<<","<<c<<")" << endl; //如果 max2 > max1 ,说明没用这个结点,则采用 max2后面的结点,删除p1带的结点 deleteNode(p1); return max2; } }
再接着,突然想起 C++的标准算法里面有个全排列的,发现用他的话,也可以很容易的写出来,不过,时间复杂度差不多。
C++ STL中提供了std::next_permutation与std::prev_permutation可以获取数字或者是字符的全排列,其中std::next_permutation提供升序、std::prev_permutation提供降序。
int new_mask[] = {1,1,1,0,0,0}; int a[] = { 3,1,11,2,7,8}; int n = sizeof(a)/sizeof(a[0]); int c = 0; for( int i = 0;i<n;i++) c+=a[i]; c = c/2; int max =0x80000000; int item[] = {-1,-1,-1,-1,-1,-1}; do { int sum = 0; for(int j = 0;j<n;j++){ if(new_mask[j]){ sum = sum+a[j]; } } if(sum < c && sum > max ){ max = sum; memcpy(item,new_mask,sizeof(new_mask)); } } while (prev_permutation(new_mask,new_mask+n)); cout << "use :"; for(int j = 0;j<n;j++){ if(item[j]){ cout << a[j] << " "; } } cout << endl; cout << "max :" << max << endl;
最后,附上第一种方法的源代码
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> using namespace std; class node{ public: int index; node * next; node(int i):index(i),next(NULL){} }; void showNode(node * p){ p = p->next; while (p){ cout << p->index <<" "; p = p->next; } cout << endl; } void deleteNode(node * &p){ node * h = p; while(h){ node * temp = h; h = h->next; delete temp; } p = NULL; } const int a[] = { 3,1,11,2,7,8}; const int n = sizeof(a)/sizeof(a[0])/2; int sum =0; int c = 0; int num = 0; int iSelectj(int i,int j,int c,node * &p){ //先判断是否超出了范围,或者数目不够了 if( c < 0 || i < j) return 0x80000000; //判断是否已经选够数字了 if( j == 0 ) return 0; //判断是否不合法 if(i < 1 ) return 0x80000000; //如果选择这个数的话,就生成这个结点 node * p1 = new node(i-1); int max1 = a[i-1]+iSelectj(i-1,j-1,c-a[i-1],p1); //不用的话,就继续用上一层传进来的结点 int max2 = iSelectj(i-1,j,c,p); cout << "iSelectj("<<i<<","<<j<<","<<c<<"):"; if( max1 > max2){ cout << "use "<< a[i - 1]<< "+iSelectj("<<i-1<<","<<j-1<<","<<c-a[i-1]<<")" << endl; //如果 max1 > max2 说明用了这个结点,那么max2后面采用的结点都不要 deleteNode(p->next); p->next = p1; return max1; }else{ cout << "use " << "iSelectj("<<i-1<<","<<j<<","<<c<<")" << endl; //如果 max2 > max1 ,说明没用这个结点,则采用 max2后面的结点,删除p1带的结点 deleteNode(p1); return max2; } } int main(void) { for( int i = 0;i<2*n;i++) sum+=a[i]; c = sum/2; node * h = new node(-1); int max = iSelectj(2*n,n,c,h); for( auto t: a) cout << t << " "; cout << endl; cout << "max = " <<max << endl; cout << "use :"; showNode(h); deleteNode(h); cout << "execute: " << num << endl; system("pause"); return 0; }