作为比较经典的算法思想,动态规划恐怕早已深入人心。今天在网上找了一个 题目来练练手。
题目大意:
给出一个 数字序列,求出最大的两个不连续子串之和 。
这道题 看似 就是 最大子序列和 的一个变种。本质都是用 DP 来解决。
首先,将 a[i] 表示第 i 个元素,再建立 三个数组 tm[num],sm[num],stm[num]
含义如下: tm[i] 表示 以第i个元素结尾的最大字串;sm[i] 表示以第i个元素开头的最大字串;
最后一个stm比较关键,stm[i] 表示后面 n-i+1 个元素的最大值
这里举个例子:
-1,2,3,4,2,5
一个6个元素,stm[2]表示后面5个元素的最大子串的值(6-2+1)
那么,下面就可以列出状态转移方程:
tm[i] = max{ tm[i-1]+a[i],a[i]}
sm[i] = max{sm[i+1]+a[i],a[i]}
stm[i] = max{stm[i+1],sm[i]} 这个方程的意思是,后面x个数的最大字串的值 = max(后面x-1个数的最大子串的值,以第i个元素开头的最大子串}
说明:状态转移方程只要合理,能解释得清楚,即符合题意。 方程不唯一。
//
// main.cpp
// Demo
//
// Created by Jaster_chen on 1/27/16.
// Copyright © 2016 Jaster_chen. All rights reserved.
//
#include <iostream>
using namespace std;
#define MAX 50001
#define max(a,b) a>b?a:b
int a[MAX],tm[MAX],sm[MAX],stm[MAX];
//tm表示以i为结尾的最大字串,sm表示以i开始的最大字串,stm[i]表示取后面 num-i+1 个元素最大值
int main()
{
int T,num,i;
scanf("%d",&T);
while(T--){
cin>>num;
for(i=1;i<=num;i++){
scanf("%d",&a[i]);
}
tm[1] = a[1];
for(i=2;i<=num;i++){
tm[i] = max(tm[i-1]+a[i],a[i]);
}
sm[num] = a[num];
for(i=num-1;i>=1;i--){
sm[i] = max(sm[i+1]+a[i],a[i]);
}
stm[num] = a[num];
for(i=num-1;i>=1;i--){
stm[i] = max(stm[i+1],sm[i]);
}
int sum;
sum = tm[1]+stm[2];
for(i=2;i<=num-1;i++){
sum = max(sum,tm[i]+stm[i+1]);
}
printf("%d\n",sum);
}
return 0;
}
代码写完之后,时间复杂度保持在 O(n),但是 在POJ上 总是通不过,说是 时间超时。
检查了好几遍,发现没问题。
终于发现是 因为 cin,cout,用scanf,printf代替就行。
所以,做 POJ,HDU,ACM的题目还是 用 scanf,printf 时间 快一点。