hdu1024(dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024
题意:给出一个有n个元素的数组,从其中选出m个不相交区间,求m个区间所有数字和最大为多少
思路:很明显dp
我们可以用dp[i][j]存储前i个区间前j个元素和的最大值,最终答案为dp[n][m];
假设当前为第i个区间,对于第j个元素,我们可以将其加入前i个区间,或者将其单独作为一个区间;
那么状态转移方程为:
dp[i][j]=max(dp[i][j-1]+a[j], max(dp[i-1][k])+a[j]) //如果我们将第j个元素加入前i个区间,那么有dp[i][j]=dp[i][j-1]+a[j],
如果将其单独作为一个区间,那么dp[i][j]=前i-1个区间最大值+a[j],即为dp[i][j]=max(dp[i-1][k])+a[j] (1<=k<=n),max(dp[i-1][k]表示前i-1个区间前j个元素能得到的最大值,那么为了得到分成i-1个区间能得到的最大值我们需要遍历一下k;
时间复杂度为 O(m*n^2)
本题的数据范围为1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767, m没有给出范围,也很显然我们上面的算法会超时 ,仔细分析一下我们可以发现max(dp[i-1][k])的值我们在之前已经计算过了,我们只需要开一个一维数组来保持前i-1个区间能得到的最大值即可;
前面的算法还有一个bug就是如果m稍大一点的话开二维数组会爆内存的,因此,我们需要将其优化成一维数组,用vis[i]表示前i-1个区间能得到的最大和;
dp[i][j]中的i可以有外面的for循环控制,那么我们用dp[j]代替前面的dp[i][j]即可;
代码:
1 #include <iostream>
2 #include <stdio.h>
3 #include <string.h>
4 #include <math.h>
5 #include <algorithm>
6 #define MAXN 1000010
7 #define INF 999999999
8 using namespace std;
9
10 int vis[MAXN], a[MAXN], dp[MAXN];
11
12 int main(void){
13 int m, n, mx;
14 while(~scanf("%d%d", &m, &n)){
15 memset(vis, 0, sizeof(vis));
16 memset(dp, 0, sizeof(dp));
17 for(int i=1; i<=n; i++){
18 scanf("%d", &a[i]);
19 }
20 for(int i=1; i<=m; i++){
21 mx=-1*INF;
22 for(int j=i; j<=n; j++){ //注意这里j是从i开始,因为前面已经分了i-1个区间,至少用了i-1个元素嘛
23 dp[j]=max(dp[j-1]+a[j], vis[j-1]+a[j]);
24 vis[j-1]=mx;
25 if(mx<dp[j]){
26 mx=dp[j];
27 }
28 }
29 }
30 printf("%d\n", mx);
31 }
32 return 0;
33 }