题目链接:
POJ 2479:http://poj.org/problem?id=2479
POJ 2593:http://poj.org/problem?id=2593
分类:动态规划
算法:两道题目除了输入格式不同以外,其它算法一样,利用双向动态规划算法。
(1)最原始的计算方法
设有数组长度为,用一个的矩阵进行和的存储,即
则计算的时间复杂度为:
接下来,需要求出不相交的两段和的最大值。
设为数组的分界,则第一段是,第二段是,的取值范围为,因此计算最大值的过程中,需要比较的次数为:
因此计算步骤为
因此渐进时间复杂度为。
(2)利用单向动态规划算法
如果原题中是求一段连续子序列的和,即
因此可以利用动态规划算法(最优子结构的证明可以找一本算法书看一下):
设表示为到第个元素为止的连续子序列的最大值和,其具有如下的最优子结构:
因此当求两段和时,首先通过分界标识将数组分为两部分,的取值范围为,再根据上述的最优子结构公式计算出第二段的最大值,再求和,最终结果即为
其中,为从第项开始利用动态规划计算的最大值。上式实质上的比较次数为一个二重循环,即为
因此渐进时间复杂度为。
(3)利用双向动态规划算法
由于是求不相交的两段连续子序列和的最大值,因此造成上述第(2)种算法耗时的主要原因在于需要用遍历的方法逐个比较最大值,因此需要双重循环。为了解决上述问题,从左至右以及从右至左分别采用动态规划,并且对函数进行改进,其保存的不是至第个元素的最大值,而是小于或等于第个元素的最大值,在如下的程序中,用MAX变量进行赋值。因此最终仅需要执行单层循环3次,因此渐进时间复杂度为。
后续工作的思考:需要对最优子结构进行数学证明。
POJ 2479 C++ 源码
1 #include2 3 using namespace std; 4 5 int A[100001] = { 0}; // 初始化数组 6 int d1[100001] = { 0}; // 状态方程,用于保存当前最大值 7 int d2[100001] = { 0}; 8 9 int solve(int n);10 11 int main()12 {13 int T,n;14 scanf("%d",&T);15 for (int j = 0; j < T; j++)16 {17 scanf("%d",&n);18 for (int i = 0; i < n; i++)19 scanf("%d",&A[i]);20 printf("%d\n",solve(n));21 }22 return 0;23 }24 25 26 int solve(int n)27 {28 // O(n)29 int current = A[0];30 d1[0] = A[0];31 int MAX = A[0];32 for (int i = 1; i < n; i++)33 {34 if (current+A[i] >= A[i]) current = current + A[i];35 else current = A[i];36 if (current <= MAX) d1[i] = MAX;37 else38 {39 MAX = current;40 d1[i] = MAX;41 }42 }43 // O(n)44 MAX = A[n-1];45 current = A[n-1];46 d2[n-1] = A[n-1];47 for (int i = n-2; i >= 0; i--)48 {49 if (current+A[i] >= A[i]) current = current + A[i];50 else current = A[i];51 if (current <= MAX) d2[i] = MAX;52 else53 {54 MAX = current;55 d2[i] = MAX;56 }57 }58 int result = d1[0] + d2[1];59 60 for (int i=1; i < n-1; i++)61 {62 if (d1[i] + d2[i+1] > result)63 result = d1[i] + d2[i+1];64 }65 return result;66 }
POJ 2593 C++ 源码
1 #include2 #include 3 4 using namespace std; 5 6 int A[100001] = { 0}; // 初始化数组 7 int d1[100001] = { 0}; // 状态方程,用于保存当前最大值 8 int d2[100001] = { 0}; 9 10 int solve(int n);11 12 int main()13 {14 int n;15 while (scanf("%d",&n) && n!=0)16 {17 for (int i = 0; i < n; i++)18 scanf("%d",&A[i]);19 printf("%d\n",solve(n));20 }21 return 0;22 }23 24 25 int solve(int n)26 {27 // O(n)28 int current = A[0];29 d1[0] = A[0];30 int MAX = A[0];31 for (int i = 1; i < n; i++)32 {33 if (current+A[i] >= A[i]) current = current + A[i];34 else current = A[i];35 if (current <= MAX) d1[i] = MAX;36 else37 {38 MAX = current;39 d1[i] = MAX;40 }41 }42 // O(n)43 MAX = A[n-1];44 current = A[n-1];45 d2[n-1] = A[n-1];46 for (int i = n-2; i >= 0; i--)47 {48 if (current+A[i] >= A[i]) current = current + A[i];49 else current = A[i];50 if (current <= MAX) d2[i] = MAX;51 else52 {53 MAX = current;54 d2[i] = MAX;55 }56 }57 int result = d1[0] + d2[1];58 59 for (int i=1; i < n-1; i++)60 {61 if (d1[i] + d2[i+1] > result)62 result = d1[i] + d2[i+1];63 }64 return result;65 }