数组分割

来源:互联网 发布:腾讯国际足球数据库 编辑:程序博客网 时间:2024/06/05 19:29

动态规划解决数组分割问题

总结遇到的数组分割问题,主要是数组能否均分,后续再补充其他情况

Leetcode 416 Partition Equal Subset Sum

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:
Each of the array element will not exceed 100.
The array size will not exceed 200.
Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].


数组可以均分成两个子数组问题,由于要对所有元素做均分,可以算出数组和,然后求和的一半T,然后判断数组中是否存在子数组和为T。

 bool canPartition(vector<int>& nums) {        int sum=0;        int dp[20005]={0};        dp[0]=1;        for(int i=0;i<nums.size();i++){            sum+=nums[i];            for(int j=sum;j>=0;j--)            {                if(dp[j]!=0)                dp[j+nums[i]]=1;            }        }        return (0==(sum&0x01))&&(1==dp[sum>>1]);    }

dp数组记录每次录入一个元素时数组和的所有可能值,Fn(jA[n])=Fn1(j)使用滚动数组优化:dp[n+nums[i]]=dp[n1]
该题目用递归做的话,会出现超时,时间复杂度为O(2n),动态规划做,时间复杂度O(nk)空间复杂度为O(k)

堆砖块问题

小易有n块砖块,每一块砖块有一个高度。小易希望利用这些砖块堆砌两座相同高度的塔。为了让问题简单,砖块堆砌就是简单的高度相加,某一块砖只能使用在一座塔中一次。小易现在让能够堆砌出来的两座塔的高度尽量高,小易能否完成呢。

输入描述:
输入包括两行:
第一行为整数n(1 ≤ n ≤ 50),即一共有n块砖块
第二行为n个整数,表示每一块砖块的高度height[i] (1 ≤ height[i] ≤ 500000)
输出描述:
如果小易能堆砌出两座高度相同的塔,输出最高能拼凑的高度,如果不能则输出-1.
保证答案不大于500000。

输入例子:
3
2 3 5

输出例子:
5


该问题是上一题的升级版,要求从数组中找出两个和相等的子数组,而且和是最大,其中元素只能被使用一次。
使用动态规划解决该问题:记录前n-1个元素所构成的两个子数组的各种可能的差值以及该差值对应的两个数组的最大值,对于第n个元素,可以有3种方法:1.加入到和更大的数组中;2.加入到和更小的数组中;3.不放入任何数组中。则对应公式:

Fn(j+A[n])=max(Fn1(j)+A[n],Fn(j+A[n]))
Fn(abs(jA[n]))=max(Fn(abs(jA[n])),max(Fn1(j),Fn1(j)j+A[n]))

Fn(j)=max(Fn(j),Fn1(j))

记录数组至少为两个,一个用来记录历史数据,一个用来更新,所以开辟大小为2的二维数组

const int maxn = 50;int dp[2][maxn];int main(){    vector<int>nums;    int tmp, n;    //freopen("in.txt", "r", stdin);    cin >> n;    while (n--)    {        cin >> tmp;        nums.push_back(tmp);    }    MST(dp, 0);    dp[0][nums[0]] = nums[0];    int p = 1;    for (int j= 1; i < nums.size(); i++){        dp[p][nums[j]] = max(dp[1-p][nums[j]],nums[j]);//0为初始值,遍历的时候可能会忽略掉,开始时候要添加进去        for (int j = 1; j <maxn; j++){            if (dp[1 - p][j]){                dp[p][j] = max(dp[p][j], dp[1 - p][j]);                dp[p][j + nums[j]] = max(dp[p][j + nums[j]], dp[1 - p][j] + nums[i]);                dp[p][abs(j - nums[j])] = max(dp[p][abs(j - nums[j])], max(dp[1 - p][j], dp[1 - p][j] - j + nums[j]));            }        }        p = 1 - p;    }    if (dp[1 - p][0])cout << dp[1 - p][0];    else cout << -1;    return 0;}