《编程之美》 2.18 数组分割
问题:有一个没有排序,元素个数为2*n的正整数数组,要求:如何能把这个数组分割成元素个数为n的两个数组,并使两个子数组的和最接近?
解法一:排序,根据下标奇偶性分组,不过结果不是最优的。
代码一:
int ArrayCut(int a[],int N)
{
qsort(a,N,sizeof(int),compare);
int sum1,sum2;
sum1 = sum2 = 0;
for ( int i = 0; i < N; i = i + 2) {
sum1 += a[i];
printf("%-4d",a[i]);
}
printf("\n");
for ( int i = 1; i < N; i = i + 2) {
sum2 += a[i];
printf("%-4d",a[i]);
}
printf("\n");
printf("Sum of even indexes: %d , sum of odd indexes: %d .\n",sum1,sum2);
}
解法二:DP解法。详见《编程之美》和代码。
代码二:
char dp[MAXNUM][MAXNUM];
int path[MAXNUM][MAXNUM];
int min(int a,int b)
{
return (a>b) ? b : a;
}
int erase(int x, int a[],int n)
{
for ( int i = 0; i < n; i++) {
if (a[i] == x) {
a[i] = 0;
return 1;
}
}
return 0;
}
int ArraySplit(int a[], int N)
{
//dp[j][s]表示可否找到j个数,使得它们的和为s
memset(dp,0,sizeof(char));
memset(path,0,sizeof(int));
dp[0][0] = 1;
qsort(a,N,sizeof(int),compare);
int sum = 0;
for (int i = 0; i < N; i++) {
sum += a[i];
}
printf("sum = %d\n",sum);
for (int i = 0; i < N; i++){
for (int j = min(i+1,N/2); j >= 1; j--) {
for( int s = 1; s <= sum >> 1; s++) {
if ((s >= a[i]) && dp[j-1][s-a[i]]) {
dp[j][s] = 1;
path[s-a[i]][s] = 1;
}
}
}
}
int i;
int halfsum;
for (i = sum >> 1; i >= 0; i--) {
if (dp[N >> 1][i]) {
printf("Sum of half of Elements : %d .\n",i);
halfsum = i;
break;
}
}
int j = N >> 1;
int sumx;
int *v = (int *)malloc(N*sizeof(int));
memcpy(v,a,N*sizeof(int));
while (--j) {
sumx = i;
while (i--) {
if (path[i][sumx] && dp[j][i] && dp[j+1][sumx] && erase(sumx-i,v,N)) {
printf ("%4d -> %-4d : %-4d\n",i,sumx,sumx - i);
break;
}
}
}
printf ("%4d -> %-4d : %-4d\n",0,i,i);
show(a,N);
// printf("\n");
return halfsum;
}
解法三:DP解法二。详见代码。
代码三:
int dp3[20][20][MAXNUM];
int max(int a,int b)
{
return (a > b) ? a : b;
}
int ArraySeparation(int arr[],int N)
{
int i , j , s;
//int dp3[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(dp3,0,sizeof(dp3));
qsort(arr,N,sizeof(int),compare);
int sum = 0;
for (i = 0; i < N; i++) {
sum += arr[i];
}
for(i = 1 ; i <= N ; ++i)
{
for(j = 1 ; j <= min(i,N >> 1) ; ++j)
{
for(s = (sum >> 1); s >= arr[i-1] ; --s)
{
dp3[i][j][s] = max(dp3[i-1][j-1][s-arr[i-1]]+arr[i-1] , dp3[i-1][j][s]);
}
}
}
//因为这为最终答案 dp[2*N][N][SUM/2+1];
i = N , j= N >> 1 , s = (sum >> 1);
while(i > 0)
{
if(dp3[i][j][s] == dp3[i-1][j-1][s-arr[i-1]] + arr[i-1]) //判定这个状态是由哪个状态推导出来的
{
//cout<<arr[i]<<" "; //取中arr[i]
//j--;
//s -= arr[i];
//printf("%-4d",arr[i-1]);
printf("%-4d",arr[i-1]);
j--;
s -= arr[i-1];
}
i--;
}
printf("\nResult : %d .\n",dp3[N][N >> 1][(sum >> 1)]);
return dp3[N][N >> 1][(sum >> 1)];
}
测试程序:
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#define MAXNUM 10000
int compare(const void*a,const void*b)
{
return -(*(int*)a - *(int*)b);
}
void show(int a[],int N)
{
for(int i = 0; i < N; i++){
printf("%-4d",a[i]);
}
printf("\n");
}
int ArrayCut(int a[],int N)
{
qsort(a,N,sizeof(int),compare);
int sum1,sum2;
sum1 = sum2 = 0;
for ( int i = 0; i < N; i = i + 2) {
sum1 += a[i];
printf("%-4d",a[i]);
}
printf("\n");
for ( int i = 1; i < N; i = i + 2) {
sum2 += a[i];
printf("%-4d",a[i]);
}
printf("\n");
printf("Sum of even indexes: %d , sum of odd indexes: %d .\n",sum1,sum2);
}
/*----------------------------------------------------------------------------*/
char dp[MAXNUM][MAXNUM];
int path[MAXNUM][MAXNUM];
int min(int a,int b)
{
return (a>b) ? b : a;
}
int erase(int x, int a[],int n)
{
for ( int i = 0; i < n; i++) {
if (a[i] == x) {
a[i] = 0;
return 1;
}
}
return 0;
}
int ArraySplit(int a[], int N)
{
//dp[j][s]表示可否找到j个数,使得它们的和为s
memset(dp,0,sizeof(char));
memset(path,0,sizeof(int));
dp[0][0] = 1;
qsort(a,N,sizeof(int),compare);
int sum = 0;
for (int i = 0; i < N; i++) {
sum += a[i];
}
printf("sum = %d\n",sum);
for (int i = 0; i < N; i++){
for (int j = min(i+1,N/2); j >= 1; j--) {
for( int s = 1; s <= sum >> 1; s++) {
if ((s >= a[i]) && dp[j-1][s-a[i]]) {
dp[j][s] = 1;
path[s-a[i]][s] = 1;
}
}
}
}
int i;
int halfsum;
for (i = sum >> 1; i >= 0; i--) {
if (dp[N >> 1][i]) {
printf("Sum of half of Elements : %d .\n",i);
halfsum = i;
break;
}
}
int j = N >> 1;
int sumx;
int *v = (int *)malloc(N*sizeof(int));
memcpy(v,a,N*sizeof(int));
while (--j) {
sumx = i;
while (i--) {
if (path[i][sumx] && dp[j][i] && dp[j+1][sumx] && erase(sumx-i,v,N)) {
printf ("%4d -> %-4d : %-4d\n",i,sumx,sumx - i);
break;
}
}
}
printf ("%4d -> %-4d : %-4d\n",0,i,i);
show(a,N);
// printf("\n");
return halfsum;
}
/*----------------------------------------------------------------------------*/
int dp3[20][20][MAXNUM];
int max(int a,int b)
{
return (a > b) ? a : b;
}
int ArraySeparation(int arr[],int N)
{
int i , j , s;
//int dp3[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(dp3,0,sizeof(dp3));
qsort(arr,N,sizeof(int),compare);
int sum = 0;
for (i = 0; i < N; i++) {
sum += arr[i];
}
for(i = 1 ; i <= N ; ++i)
{
for(j = 1 ; j <= min(i,N >> 1) ; ++j)
{
for(s = (sum >> 1); s >= arr[i-1] ; --s)
{
dp3[i][j][s] = max(dp3[i-1][j-1][s-arr[i-1]]+arr[i-1] , dp3[i-1][j][s]);
}
}
}
//因为这为最终答案 dp[2*N][N][SUM/2+1];
i = N , j= N >> 1 , s = (sum >> 1);
while(i > 0)
{
if(dp3[i][j][s] == dp3[i-1][j-1][s-arr[i-1]] + arr[i-1]) //判定这个状态是由哪个状态推导出来的
{
//cout<<arr[i]<<" "; //取中arr[i]
//j--;
//s -= arr[i];
//printf("%-4d",arr[i-1]);
printf("%-4d",arr[i-1]);
j--;
s -= arr[i-1];
}
i--;
}
printf("\nResult : %d .\n",dp3[N][N >> 1][(sum >> 1)]);
return dp3[N][N >> 1][(sum >> 1)];
}
/*----------------------------------------------------------------------------*/
int main()
{
int N = 10;
int a[10];
srand(time(NULL));
for (int i = 0; i < N; i++) {
a[i] = rand() % 15 + 1;
}
show(a,N);
//ArrayCut(a,N);
ArraySplit(a,N);
ArraySeparation(a,N);
show(a,N);
}
测试输出:
8 6 7 4 12 10 12 9 6 6
sum = 80
Sum of half of Elements : 40 .
36 -> 40 : 4
30 -> 36 : 6
24 -> 30 : 6
12 -> 24 : 12
0 -> 12 : 12
12 12 10 9 8 7 6 6 6 4
4 6 6 12 12
Result : 40 .
12 12 10 9 8 7 6 6 6 4
REF:
1,编程之美 2.18 数组分割
2,http://www.cnblogs.com/freewater/archive/2012/08/23/2652974.html
3,http://blog.csdn.net/njufeng/article/details/11682751