#include <iostream> using namespace std; //有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。 int arr[] ={0,1,5,7,8,9,6,3,11,20,17}; const int N=5; const int SUM = 87; int split1() { int dp[2*N+1][N+1][SUM/2+2]; /* 用dp(i,j,c)来表示从前i个元素中取j个、且这j个元素之和不超过c的最佳(大)方案,在这里i>=j,c<=S 状态转移方程: //限第i个物品 不取 dp(i,j,c)=max{dp(i-1,j-1,c-a[i])+a[i],dp(i-1,j,c)} dp(2N,N,SUM/2+1)就是题目的解。 */ //初始化 memset(dp,0,sizeof(dp)); for(int i=1;i<=2*N;i++) for(int j=1;j<=min(i,N);j++) for(int s= SUM/2+1;s>=arr[i];s--) { dp[i][j][s] = max(dp[i-1][j-1][s-arr[i]]+arr[i],dp[i-1][j][s]); } //因为这为最终答案 dp[2*N][N][SUM/2+1]; int i=2*N; int j=N; int s=SUM/2+1; while(i>0) { if(dp[i][j][s]==dp[i-1][j-1][s-arr[i]]+arr[i])//判定这个状态是由哪个状态推导出来的 { cout<<arr[i]<<" ";//取中arr[i] j--; s-=arr[i]; } i--; } cout<<endl; return dp[2*N][N][SUM/2+1]; } int split2() { int dp[N+1][SUM/2+2];//取N+1件物品,总合不超过SUM/2+2,的最大值是多少 memset(dp,0,sizeof(dp));//初始状态都为 0 for(int i=1;i<=2*N;i++) for(int j=1;j<=min(i,N);j++) for(int s= SUM/2+1;s>=arr[i];s--)//0 1背包从大到小,可以省空间,即最外层的空间 { dp[j][s] = max(dp[j-1][s-arr[i]]+arr[i],dp[j][s]); } //要求最优解则 空间不能优化, return dp[N][SUM/2+1]; } int split3() { int flag[N+1][SUM/2+2]; //取N+1件物品,总合为SUM/2+2是否合法,即N+1件物品的总合不能不刚好是SUM/2+2 memset(flag,0,sizeof(flag));//都不合法 //注意初始化 flag[0][0] = 1; //可以,取0件物品,总合为0,是合法的 for(int i=1;i<=2*N;i++) for(int j=1;j<=min(i,N);j++) for(int s = SUM/2+1;s>=arr[i];s--) //从大到小,数组少了一维 { if (flag[j-1][s-arr[i]]) flag[j][s] = 1; } for(int s = SUM/2+1;s>=0;s--) if(flag[N][s]) return s; //要求最优解则 空间不能优化, return 0; } int main() { int s1 = split1(); int s2 = split2(); int s3 = split3(); cout<<"s1="<<s1<<" s2="<<s2<<" s3="<<s3<<endl; system("pause"); return 0; }