数组分割

来源:互联网 发布:mysql 查看表结构 编辑:程序博客网 时间:2024/05/16 06:11

《编程之美》 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 10000int 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 = 80Sum 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




0 0
原创粉丝点击