zoukankan      html  css  js  c++  java
  • 编程之美——数组分割

    一、题目概述:有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。
    假设数组A[1..2N]所有元素的和是SUM。模仿动态规划解0-1背包问题的策略,令S(k, i)表示前k个元素中任意i个元素的和的集合。显然:
    S(k, 1) = {A[i] | 1<= i <= k}
    S(k, k) = {A[1]+A[2]+…+A[k]}
    S(k, i) = S(k-1, i) U {A[k] + x | x属于S(k-1, i-1) }
    按照这个递推公式来计算,最后找出集合S(2N, N)中与SUM/2最接近的那个和,这便是答案。这个算法的时间复杂度是O(2^N).
    因为这个过程中只关注和不大于SUM/2的那个子数组的和。所以集合中重复的和以及大于SUM/2的和都是没有意义的。把这些没有意义的和剔除掉,剩下的有意义的和的个数最多就是SUM/2个。所以,我们不需要记录S(2N,N)中都有哪些和,只需要从SUM/2到1遍历一次,逐个询问这个值是不是在S(2N,N)中出现,第一个出现的值就是答案。我们的程序不需要按照上述递推公式计算每个集合,只需要为每个集合设一个标志数组,标记SUM/2到1这个区间中的哪些值可以被计算出来。关键代码如下:

    #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;
    
    // 模仿动态规划解0-1背包问题的策略
    int solve1()
    {
        int i , j , s;
        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(i = 1 ; i <= 2*N ; ++i)
        {
            for(j = 1 ; j <= min(i,N) ; ++j)
            {
                for(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];
        i=2*N , j=N , 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 solve2()
    {
        int i , j , s;
        int dp[N+1][SUM/2+2];     //取N+1件物品,总合不超过SUM/2+2,的最大值是多少 
        memset(dp,0,sizeof(dp));    //初始状态都为0
    
        for(i = 1 ; i <= 2*N ; ++i)
        {
            for(j = 1 ; j <= min(i,N) ; ++j)
            {
                for(s = SUM/2+1 ; s >= arr[i] ; --s)    //01背包从大到小,可以省空间,即最外层的空间
                {
                    dp[j][s] = max(dp[j-1][s-arr[i]]+arr[i] , dp[j][s]); 
                }
            }
        }
        //要求最优解则 空间不能优化,
        return dp[N][SUM/2+1];
    }
    
    int solve3()
    {
        int i , j , s;
        int isOK[N+1][SUM/2+2]; //isOK[i][v]表示是否可以找到i个数,使得它们之和等于v
        memset(isOK,0,sizeof(isOK));    //都不合法
        //注意初始化
        isOK[0][0] = 1; //可以,取0件物品,总合为0,是合法的
    
        for(i = 1 ; i <= 2*N ; ++i)
        {
            for( j = 1 ; j <= min(i,N) ; ++j)
            {
                for(s = SUM/2+1 ; s >= arr[i] ; --s) //从大到小,数组少了一维
                {
                    if( isOK[j-1][s-arr[i]] )
                        isOK[j][s] = 1;
                }
            }
        }
        for(s = SUM/2+1 ; s >= 0 ; --s)
        {
            if(isOK[N][s])
                return s;
        }
    
        //要求最优解则空间不能优化
        return 0;
    }
    
    int main(void)
    {
        int s1 = solve1();
        int s2 = solve2();
        int s3 = solve3();
        cout<<"s1="<<s1<<endl;
        cout<<"s2="<<s2<<endl;
        cout<<"s3="<<s3<<endl;
        system("pause");
        return 0;
    }
  • 相关阅读:
    把影响集中到一个点
    How to avoid Over-fitting using Regularization?
    适定性问题
    Numerical Differentiation 数值微分
    What Every Computer Scientist Should Know About Floating-Point Arithmetic
    Generally a good method to avoid this is to randomly shuffle the data prior to each epoch of training.
    What is the difference between iterations and epochs in Convolution neural networks?
    Every norm is a convex function
    Moore-Penrose Matrix Inverse 摩尔-彭若斯广义逆 埃尔米特矩阵 Hermitian matrix
    perl 类里的函数调用其他类的函数
  • 原文地址:https://www.cnblogs.com/wuchanming/p/4457358.html
Copyright © 2011-2022 走看看