51nod 求子段和问题总结(DP)
来源:互联网 发布:幽默app软件下载 编辑:程序博客网 时间:2024/06/05 19:33
1049 最大子段和
基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注
N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出最大子段和。
Input示例
6
-2
11
-4
13
-5
-2
Output示例
20
最基本的问题
#include <iostream>#include <cstdio>#include <algorithm>#include <string>using namespace std;int main(){ long long n,a,ans = -1e9,temp_sum = 0; cin>>n; for(int i = 0; i < n; i++){ cin>>a; if(temp_sum < 0) temp_sum = a; else temp_sum += a; ans = max(ans,temp_sum); } cout<<ans<<endl; return 0;}
1050 循环数组最大子段和
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注
N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
Output
输出循环数组的最大子段和。
Input示例
6
-2
11
-4
13
-5
-2
Output示例
20
这个是上面的变形,这个我们还要求出最小子段和,然后用总和减去最小子段和比较最大子段和,看谁大就行
#include <iostream>#include <cstdio>#include <algorithm>#include <string>using namespace std;int main(){ long long n,a,ans1 = 0,ans2 = 0,maxsum =0,minsum = 0, sum = 0; cin>>n; for(int i = 0; i < n; i++){ cin>>a; sum += a; if(maxsum < 0) maxsum = a; else maxsum += a; if(minsum > 0) minsum = a; else minsum += a; ans1 = max(maxsum,ans1); ans2 = min(minsum,ans2); } ans1 = ans1 > sum - ans2 ? ans1 : sum - ans2; cout<<ans1<<endl; return 0;}
1065 最小正子段和
基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注
N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子序列(a[i],a[i+1],…a[j]),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的。
例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和为1,是最小的。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数
Output
输出最小正子段和。
Input示例
8
4
-1
5
-2
-1
2
6
-2
Output示例
1
这题没想像中那么简单,开始以为和求最大的差不多,后来随手捏了几个数据都不行,之前是方法是累加,现在要区间求
先求一下从第一位开始的到第i位的累加,
4,-1,5,-2,-1,2,6,-2 => 4 3 8 6 5 7 13 11
对这个累加的数列排个序,然后只要判断邻近的两个数是否可以组成序列,比如4和3就不可以,因为4 > 3而4对应下标为0,3对应为1。4和5就可以
解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解。
#include <iostream>#include <cstdio>#include <algorithm>#include <string>#include <vector>using namespace std;vector<pair<long long,int> > vec;int main(){ int n,a,ans = 1e9; long long sum = 0; cin>>n; vec.push_back(make_pair(0,0)); for(int i = 1; i <= n; i++){ cin>>a; sum += a; vec.push_back(make_pair(sum,i)); } sort(vec.begin(), vec.end()); for(int i = 1; i <= n; i++){ if(vec[i].second > vec[i-1].second){ int subsum = vec[i].first - vec[i-1].first; if(subsum > 0){ ans = min(ans,subsum); } } } cout<<ans<<endl; return 0;}
1052 最大M子段和
基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注
N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。
例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
Input
第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 5000)
第2 - N+1行:N个整数 (-10^9 <= a[i] <= 10^9)
Output
输出这个最大和
Input示例
7 2
-2
11
-4
13
-5
6
-2
Output示例
26
dp[i][j] 表示在前 i 个数中选取 j 组 且第 i 个数在最后一组中的 Max Sum Plus Plus
那么现在对于第i个数有两种决策
1, 第 i 个数和第 i-1 个数连接成一段 dp[i][j] = dp[i-1][j] + a[i]
2, 第 i 个数自己单独做一段 那么前面就需要有 j-1 段 dp[i][j] = max{dp[k][j-1] | j - 1<= k < i} + a[i]
那么也就有了状态转移方程 dp[i][j] = max(dp[i-1][j], max{dp[k][j-1] | j - 1<= k < i}) + a[i];
但是这样空间复杂度超了,可以用滚动数组优化
发现更新 dp[i][j] 的时候只用到了 dp[.][j] 和 dp[.][j-1] 里面的值 也就是 dp[.][0~j-2] 都已经没用了 那也就不用存这些没用的了 也就是j只用开两维就够了 也就是所谓的滚动数组了 用 dp[.][1] 和 dp[.][0] 来轮换表示 dp[.][j] 和 dp[.][j-1] 这样空间复杂度就降到了O(n) 这是可以接受的
那么我现在用两个数组来优化
用两个数组,pre[MAXN]和dp[MAXN]。
首先m次循环,第x次循环代表的是把整个序列分成x个子段所能得到的最大x子段和。
pre[i]数组记录的是,从第1个数到第i个数被分成x个子段所能得到的最大子段和。
假设当前已分成了x个子段(即最外层循环执行了x次),然后需要执行第x+1次时:
则,dp[i]的转移是从下列两种情况取max值。
1.前i-1个数分为x个子段得到的最大和pre[i-1]加上单独把input[i]作为第x+1个子段的开头;
2.从前i-1个数中已经分出了x+1个子段,且第x+1个子段的尾部需要加上input[i],即dp[i-1]+input[i]
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <limits.h>using namespace std;typedef long long LL;#define MAXN 5010const LL MIN_INF = -(1 << 30);LL dp[MAXN], input[MAXN], pre[MAXN];int n, m;int main() { scanf("%d%d", &n, &m); dp[0] = MIN_INF; pre[0] = 0; for(int i = 1; i <= n; i++) { scanf("%lld", &input[i]); pre[i] = 0; dp[i] = MIN_INF; } LL ans = MIN_INF; while(m--) { ans = MIN_INF; for(int i = 1; i <= n; i++) { if(i == 1) dp[i] = pre[i - 1] + input[i]; else { if(dp[i - 1] > pre[i - 1]) { dp[i] = dp[i - 1] + input[i];//第i个数与它的上一个数在同一子段内 } else { dp[i] = pre[i - 1] + input[i];//从第i个分出一个新的子段 } } //dp[i] = max(dp[i - 1], pre[i - 1]) + input[i]; 或者直接这么取值也可 pre[i - 1] = ans; ans = max(ans, dp[i]); } pre[n] = ans; } printf("%lld\n", ans); return 0;}
然后这题还要听说O(n)的算法,然而并不会….
- 51nod 求子段和问题总结(DP)
- 51NOD 最大子字段和问题(DP入门)
- 51nod 1101 找零问题 dp (01背包问题)
- 51nod 1084 矩阵取数问题 V2(dp)
- 【51Nod】1083 - 矩阵取数问题(dp)
- 【51Nod】-1083 矩阵取数问题(DP)
- 51Nod 1083 矩阵取数问题 (DP)
- 51Nod-石子归并问题(DP解法)
- 51nod 1083 矩阵取数问题(基础dp)
- 51nod 1086 背包问题V2 (巧妙dp,二进制)
- 51nod 1002 数塔取数问题(简单DP)
- 51Nod 1083 矩阵取数问题(基础DP)
- 51nod 多重背包问题 (dp)
- 51nod 1092 回文字符串 dp问题
- 51nod 活动安排问题 《贪心+dp》
- 51nod 编辑距离问题 dp
- 51nod 1002 数塔取数问题(基础dp)
- 51Nod-1002 数塔取数问题【DP】
- 数据结构---抽象建模
- socket通信之九:使用完成端口实现的一个聊天室
- 哲学家就餐问题
- [刷题]Hash Function
- 犀牛——第8章函数 8.7函数属性、方法和构造函数
- 51nod 求子段和问题总结(DP)
- python & opencv 环境搭建
- static变量与全局、局部变量的区别 !
- 【转】如何使用Notification功能
- ubuntu发出奇怪的相声
- ListView
- python字符串:
- 《剑指Offer》面试题:反转链表
- HttpServlect详情