DP问题
解题步骤已经在代码中解释的很清楚了
DP题的代码个人认为是挺难理解的,但是,如果理解了就会觉得很简单
需要注意一下的是,题目让分两段,必须就得分两段,即使分成两段之后的总和反而更小
比如代码注释中我举得例子
package 动态规划;
import java.util.Scanner;
public class _1024_选择多次最长子序列 {
/**
* m=3,n=6 | -1 | 4 | -2 | 3 | -2 | 3 |
*
* now: | 0 | -1 | 4 | 2 | 5 | 3 | 6 |
*
* pre: |-99 | -1 | 4 | 4 | 5 | 5 | 0 |
*
* now: | 0 | -1 | 3 | 2 | 7 | 5 | 8 |
*
* pre: |-99 |-99 | 3 | 3 | 7 | 7 | 0 |
*
* now: | 0 | -1 | 3 | 1 | 6 | 5 | 10 |
*
* pre: |-99 |-99 |-99| 1 | 6 | 6 | 0 |
*
* @param args
*/
// http://acm.hdu.edu.cn/showproblem.php?pid=1024
// 状态: dp[i][j] --- 表示前j个数中的最大i段子段和,并且a[j]包涵于最后一个子段
// 状态转移方程: dp[i][j]=max{dp[i][j-1]+A[j],dp[i-1][t]+a[j] (i-1<=t<n-m+i) }
// 关于状态转移方程的解释:
// dp[i][j]由两种情况得到,
// 一、a[j]包涵于最后一个子段,这种情况的最大值就是EDP[i][j-1]+a[j];
// 二、a[j]就是最后一个子段,这种情况的最大值是 dp[i-1][t]+a[j] (i-1<=t<=n-m+i) 中
// 的最大值.
// 以下用滚动数组进行DP
// 在求 dp[i][j]时也顺便把 max{dp[i - 1][t]} ( i - 1 <= t < j) 求出来,这样的话
// 时间复杂度仅为 O(N*(N - M + 1)) , 空间为 O( N )
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int m = sc.nextInt();
int n = sc.nextInt();
// 输入
int[] num = new int[n + 1];
// 滚动数组
// now[j]代表在加上num[j]这个数之后最大可能的值,可能会更小
int[] now = new int[n + 1];
// pre[j]代表在num[j+1]这个数之前的最优值
int[] pre = new int[n + 1];
// 输入
for (int i = 1; i <= n; i++) {
num[i] = sc.nextInt();
}
// 初始化max_pre
int max_pre = 0;
// 遍历m次
for (int i = 1; i <= m; i++) {
// 刚开始max_pre值为整数型最小值
max_pre = -Integer.MAX_VALUE;
// 遍历i→n
//这里j=i的意思是,前边i个数已经没得选了,因为必定得分段,即必须得选上
//注意,now[j],j=i时,这个数也是不得不选的
//从j开始,也是为了让pre[j-1]是负最大值,j=i+1时用得着
//表示下一个i,第i+1个数之前没得选择,它前边只能是i个值相加
//之后就有一定的选择性了
//比如,运行程序,测试一下:
// 1 2 -1 4
// 4
// 2 2 -1 4
// 3
//可见,第二次 两个都选上了,虽然结果还不如选一段
for (int j = i; j <= n; j++) {
//now不一定是最优的,now是在不断的尝试now或是max_pre加新的值
now[j] = max(now[j - 1] + num[j], pre[j - 1] + num[j]);
//每一个pre[j-1]都是对于第i次,第j个数之前最优的
pre[j - 1] = max_pre;
//每行的最后一个数不一定是最大的,max_pre才是
if (now[j] > max_pre) {
//max_pre取当前最大值
max_pre = now[j];
}
}
}
System.out.println(max_pre);
}
}
public static int max(int a, int b) {
if (a > b)
return a;
return b;
}
}