数组分割(动态规划问题)

编程之美上的伪代码实在是理解不了,以下是转载的一位朋友的详解,感觉不错,基本明白了。

题目概述:有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。(啃了好久才啃明白,主要是动态规划可惜忘了,第一眼看不懂的童鞋洗洗睡睡明天继续研究哈,更多精彩请看《编程之美》)

第一想法:

是从2N个数的数组中提取所有N的组合情况,估计需要N个for循环,此时至少为N的阶乘的时间复杂度;然后想到动态规划的0-1背包(其实是看了原文才晓得的),将heap[M](M表示从2N中所有可能的M个元素和组成的集合),从下到上(m->1...->N)最终求的完整的heap[N]。要点:可以想成求不大于sum/2的最接近集合,以下为分析:

假设数组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最接近的那个和,这便是答案。

第二想法:
第一种算法时间复杂度随着N的增大而“狠大”,注意是很大,所以出现了第三种方法(书上说时间复杂度为n的平方乘以num),根据分析可以去求不大于sum/2的所有数能否用从2N中的N个提取的数组合出来,标记所有可能,把下面代码看懂就真懂了(看懂了就不难咯),代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <algorithm>
 
using namespace std;
 
#define MAXN 101
#define MAXSUM 100000
int A[MAXN];
bool dp[MAXN][MAXSUM];
 
// 题目可转换为从2n个数中选出n个数,其和尽量接近于给定值sum/2
int main()
{
     int n, i, k1, k2, s, u;
     cin >> n;
     for (i=1; i<=2*n; i++)
         cin >> A[i];
     int sum = 0;
     for (i=1; i<=2*n; i++)
         sum += A[i];
     memset (dp,0, sizeof (dp));
     dp[0][0]= true ;
     // 对于dp[k][s]要进行u次决策,由于阶段k的选择受到决策的限制,
     // 这里决策选择不允许重复,但阶段可以重复,比较特别
     for (k1=1; k1<=2*n; k1++) //              // 外阶段k1
         for (k2=min(k1,n); k2>=1; k2--)      // 内阶段k2
             for (s=1; s<=sum/2; s++) // 状态s
                 // 有两个决策包含或不包含元素k1
                 if (s>=A[k1] && dp[k2-1][s-A[k1]]) //判断总和为s的数是否能够等于k2个提取的数之和
                     dp[k2][s] = true ;
     // 确定最接近的给定值sum/2的和
     for (s=sum/2; s>=1 && !dp[n][s]; s--);
     printf ( "the differece between two sub array is %d\n" , sum-2*s);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划分割回文串是一种常用的解决方案。在动态规划中,我们可以使用不同的状态定义和状态转移方程来解决这个问题。 一种常见的状态定义是使用一维数组dp[i],其中dp[i]表示字符串s的前i个字符形成回文子串的最少分割次数。这种定义可以通过判断s[j:i]是否为回文来进行状态转移,其中1 <= j <= i。具体的状态转移方程可以如下表示: - 当s[0:i]本身就是一个回文串时,不需要进行分割,即dp[i] = 0。 - 否则,我们可以遍历所有可能的分割点j,如果s[j+1:i]是回文串,那么我们可以将问题分割为两部分,即dp[i] = min(dp[i], dp[j] + 1)。 另一种状态定义是使用二维数组dp[i][j],其中dp[i][j]表示字符串s的前i个字符分割为j个子串的修改的最小字符数。在这种定义下,我们可以使用类似的状态转移方程来进行计算。具体的状态转移方程可以如下表示: - 当i < j时,不可能将前i个字符分割为j个子串,即dp[i][j] = INF。 - 当i >= j时,我们可以遍历所有可能的分割点k,计算dp[i][j]的最小值,即dp[i][j] = min(dp[i][j], dp[k][j-1] + cost(k+1, i)),其中cost(k+1, i)表示将子串s[k+1:i]修改为回文所需的最小字符数。 这两种定义和状态转移方程都可以用来解决动态规划分割回文串的问题,具体使用哪种方法取决于具体的问题要求和效率要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [动态规划解决回文串问题](https://blog.csdn.net/qq_37414405/article/details/111317301)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [动态规划分割回文串](https://blog.csdn.net/melody157398/article/details/119769501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值