hdoj 1003 Max Sum---动态规划,最大子序列求和

2024-06-13 20:38

本文主要是介绍hdoj 1003 Max Sum---动态规划,最大子序列求和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

初来乍到,动态规划不会呀,刚开始用暴力法,超时了!超时代码如下:

思路:大致是选中一个数当做结尾,然后加和。例如在6 -1 5 8 -7选中5作为结尾大致有如下序列

6  -1   5

    -1   5

          5

就这样遍历所以的结尾,结果超时。代码如下:

#include<iostream>
#include<algorithm>
using namespace std;#define VALUE 101
#define INF 999999999int D[VALUE];
int n;
int t;
int sum;
int maxSum[VALUE][VALUE]={0};
int  x,y;
int T=1;
int main() {int i, j, k;cin >> n;while (n--) {cin >> t;for (i = 1; i <= t; i++) {cin >> D[i];}for (i = 1; i <= t; i++) { //选那个结束for (j = 1; j <= i; ++j) {  //有多少行sum = 0;for (k = j; k <= i; k++) {  //每一个行有多少个相加sum = sum + D[k];}maxSum[j][i] = sum;}}int max = -INF;for (i = 1; i <= t; i++) {for (j = 1; j <= i; j++) {if (max < maxSum[j][i]) {max = maxSum[j][i];x = j;y = i;}}}cout << "Case " << T << ":" << endl;T++;cout << max << " " << x << " " << y << endl;if (n != 0) {cout << endl;}}
}

      在网上搜了代码。大多是只说是动态规划经典问题、求最大子序列和,终于算是有了眉目想通了。

对于整个序列a[n]来说,它的所有子序列有很多很多,但是可以将它们归类。
注意,是以**结尾的子序列,其中肯定是要包含**的了
 
以a[0]结尾的子序列只有a[0]
以a[1]结尾的子序列有    a[0]a[1]和a[1]
以a[2]结尾的子序列有    a[0]a[1]a[2]   /  a[1]a[2]  / a[2]
……
以a[i]结尾的子序列有a[0]a[1]……a[i-2]a[i-1]a[i]   / a[1]a[2]……a[i-2]a[i-1]a[i]  /  a[2]a[3]……a[i-2]a[i-1]a[I]  / …… /  a[i-1]a[i] / a[i]
 
所有以a[0] ~a[n]结尾的子序列分组构成了整个序列的所有子序列。
 
这样,我们只需求以a[0]~a[n]结尾的这些分组的子序列中的每一分组的最大子序列和。然后从n个分组最大子序列和中选出整个序列的最大子序列和。
 
观察可以发现,0,1,2,……,n结尾的分组中,
maxsum a[0] = a[0]
maxsum a[1] = max( a[0] + a[1] ,a[1])  = max( maxsum a[0] + a[1] ,a[1]) 
maxsum a[2] = max( max ( a[0] + a[1] + a[2],a[1] + a[2] ),a[2])  
= max(  max( a[0] + a[1] ,a[1]) + a[2] , a[2]) 
= max(  maxsum a[1] + a[2] , a[2])
……
依此类推,可以得出通用的式子。
maxsum a[i] = max( maxsum a[i-1] + a[i],a[i])
 

最大子段和:给定一个序列(元素可正可负),找出其子序列中元素和最大的值。

我们用dp(i)表示序列中以元素ans(i)结尾的序列的最大子段和,那么有:dp(i)=max(dp(i-1)+ans(i),ans(i));

首先来看一道比较基础的最大子段和问题--HDOJ 1003   Max Sum:

#define INF 999999999
int solve(int ans[],int n)  
{  dp[0]=0;  int nmax=-INF;  for(int i=1;i<=n;i++)  {  dp[i]=max(dp[i-1]+ans[i],ans[i]);  nmax=max(nmax,dp[i]);  }  return nmax;  
}  

题目大意:给定一个序列,让求出此序列的最大子段和,并输出该子序列在原序列中的位置。

这里我们用变量sum来代替dp()数组,如果sum+a(i)<a(i),就更新sum的值,并重新纪录head的值,如果sum+a(i)>=a(i),则只需更新sum的值;

代码如下:

#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;#define  MAX 100005
int n;
int t;
int a[MAX]={0};
int main() {int i, j, k;cin >> t;int head, tail;int sum, max;int x;int T = 0;while (t--) {T++;cin >> n;for (i = 1; i <= n; i++) {cin >> a[i];}head = tail = x = 1;sum = max = a[1];for (i = 2; i <= n; i++) {if (sum + a[i] < a[i]) {   //重新定义开始的头x = i;sum = a[I];  //会导致sum大于max} else {sum = sum + a[i];}if (sum > max) {  //一般sum和max相等,sum大于max,说明加到一个正数。或者上一次循环中sum等于max,更新head和tail。max = sum;tail = i;head = x;}}cout << "Case " << T << ":" << endl << max << " " << head << " " << tail << endl;if (t) {cout << endl;}}
}

其他人代码:
    #include <iostream>  using namespace std;  int main()  {  int j,i,k,n,m,t;  int a[100002];  scanf("%d",&t);  for (j=1;j<=t;j++)  {  scanf("%d",&n);  for (i=0;i<n;i++)  {  scanf("%d",&a[i]);  }  int sum=0,maxsum=-1001,first =0, last = 0, temp = 1;  for (i=0;i<n;i++)  {  sum += a[i];  if (sum > maxsum)  {  maxsum = sum;first = temp;last = i+1;  }  if (sum < 0)  {  sum = 0;temp = i+2;  }  }  printf("Case %d:\n%d %d %d\n",j,maxsum,first,last);  if (j!=t)  {  printf("\n");  }  }  return 0;  } 
对于整个序列a[n]来说,它的所有子序列有很多很多,但是可以将它们归类。
注意,是以**结尾的子序列,其中肯定是要包含**的了
 
以a[0]结尾的子序列只有a[0]
以a[1]结尾的子序列有 a[0]a[1]和a[1]
以a[2]结尾的子序列有 a[0]a[1]a[2] / a[1]a[2] / a[2]
……
以a[i]结尾的子序列有a[0]a[1]……a[i-2]a[i-1]a[i]  / a[1]a[2]……a[i-2]a[i-1]a[i] /  a[2]a[3]……a[i-2]a[i-1]a[i] / …… /  a[i-1]a[i] / a[i]
 
所有以a[0] ~a[n]结尾的子序列分组构成了整个序列的所有子序列。
 
这样,我们只需求以a[0]~a[n]结尾的这些分组的子序列中的每一分组的最大子序列和。然后从n个分组最大子序列和中选出整个序列的最大子序列和。
 
观察可以发现,0,1,2,……,n结尾的分组中,
maxsum a[0] = a[0]
maxsum a[1] = max( a[0] + a[1] ,a[1])  =  max( maxsum a[0] + a[1] ,a[1]) 
maxsum a[2] = max( max ( a[0] + a[1] + a[2],a[1] + a[2] ),a[2])  
= max(  max( a[0] + a[1] ,a[1]) + a[2] , a[2]) 
= max(  maxsum a[1] + a[2] , a[2])
……
依此类推,可以得出通用的式子。
maxsum a[i] = max( maxsum a[i-1] + a[i],a[i])
 
 
用递归……当然,不递归也应该是可以解决的。
 
我们从maxsum  a[0]开始算起。
以后的每个就是  maxsum a[i-1] + a[i] 和 a[i] 中取大的那个
 
程序中判断 前一个的最大子序列和小于零时,将其置为0,然后再加a[i] ,这样不就是和a[i] 一样大的么;前一个的最大子序列和只要大于零,那么再加上a[i] 肯定比 a[i] 要大,这样,带有归零的这个 maxsum a[i-1] + a[i] 就是以表示当前位置结束的子序列的最大和了。
 
 
剩下的就是要判断起始和终点位置了。
 
在循环的过程中,每循环一次就算出一个以当前位置结束的最大子序列和。每次循环中最大的那个保存下来,不就是最终所有最大子序列和中的最大值了么。
 
其中temp保存的是前一个位置的最大子序列和的开始位置(题目中是从1开始的哦);当 sum > maxsum 时( 程序中的条件,与说明时的maxsum不太一样哦 )就记录最大值,并保持它的开始位置为temp,终止位置即为当前位置(i +1是因为题目中第一个为1,而不是0);
 
当最大子序列和小于0时,将 temp = i + 2; 其中 i + 1 表示当前位置(理由如上),i + 2就表示当前位置的下一个位置了。既此最大子序列和为负值,那么下一个的最大子序列和应该是它本身,而不再累加前边的。
 
程序中就两个if 语句,想要说明白还真不容易。
 
还有,有人会问,当整个序列全是负数时,还对吗?负数也是成立的,如果全是负数的时候,它就是每次都只取当前值作为最大值了,因为负的跟负的不就越加越小了吗。
 
因为题目中给出的范围是-1000 ~1000,所以 这里初始的maxsum 初始化为-1001 ,只有比所有可能的值都小时才行。maxsum初始化为0;那么当序列全是负数时,得出的最大值将是0……这就wrong了
 
 总之,只要上一个子序列最大和为正,那么无论当前值的正负,都会与当前的相加,这样以当前值结尾的子序列最大和就会增大。(一个正数 加一个 正数2 或者负数 那么都会比这个正数2 或负数原来要增大,同理,一个负数加任何一个数,都会使这个数减小,因此当前一子序列最大和小于零时,我们就归零它了,相当于是不加任何数,而保留当前位置值本身)
 

内存优化版:
    理解了以上思想后,观察上一个代码我们发现,那个a[10000]基本上就没用啊,保存了一些输入数据,可是那些数据只用了一次就没用了。输入数据的for循环和处理数据的for循环是一模一样的,而且处理数据也只是用到当前输入的数据。
于是,数组也可以省去了,直接将两个循环合并。输入一个数据,直接累加……省下不少空间哦。
复制代码
 1     #include <iostream>  
 2     using namespace std;  
 3     int main()  
 4     {  
 5         int j,i,k,n,m,t;  
 6         int a;  //不需要数组,只需要一个输入变量
 7         scanf("%d",&t);  
 8         for (j=1;j<=t;j++)  
 9         {  
10             scanf("%d",&n);  
11             int sum=0,maxsum=-1001,first =0, last = 0, temp = 1;  
12             for (i=0;i<n;i++)  
13             {  
14                 scanf("%d",&a); 
15                 sum += a;  
16                 if (sum > maxsum)  
17                 {  
18                     maxsum = sum;first = temp;last = i+1;  
19                 }  
20                 if (sum < 0)  
21                 {  
22                     sum = 0;temp = i+2;  
23                 }  
24             }  
25       //注意格式,我就因为将冒号写到了数的前边而wrong answer,郁闷半天才发现……
26             printf("Case %d:\n%d %d %d\n",j,maxsum,first,last);  
27             if (j!=t)  
28             {  
29                 printf("\n");  
30             }  
31         }  
32           
33         return 0;  
34     }  
复制代码

 

这篇关于hdoj 1003 Max Sum---动态规划,最大子序列求和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1058365

相关文章

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

【杂记-浅谈DHCP动态主机配置协议】

DHCP动态主机配置协议 一、DHCP概述1、定义2、作用3、报文类型 二、DHCP的工作原理三、DHCP服务器的配置和管理 一、DHCP概述 1、定义 DHCP,Dynamic Host Configuration Protocol,动态主机配置协议,是一种网络协议,主要用于在IP网络中自动分配和管理IP地址以及其他网络配置参数。 2、作用 DHCP允许计算机和其他设备通

JavaWeb系列六: 动态WEB开发核心(Servlet) 上

韩老师学生 官网文档为什么会出现Servlet什么是ServletServlet在JavaWeb项目位置Servlet基本使用Servlet开发方式说明快速入门- 手动开发 servlet浏览器请求Servlet UML分析Servlet生命周期GET和POST请求分发处理通过继承HttpServlet开发ServletIDEA配置ServletServlet注意事项和细节 Servlet注

IPD推行成功的核心要素(十一)技术规划与平台规划促进公司战略成功

随着外部大环境的影响,各企业仅有良好的愿望是不够的。预测并顺应新兴市场和技术的变化,变危机为转机,不断推出强大的产品才是一个公司持续繁荣的根本保障。而高效的产品开发往往是基于某些关键技术,针对市场推出的一个或几个产品系列,这些产品系列通常共用一些产品平台,共用一种或者几种关键技术。当一家企业进入了平稳发展期,已经建立了较为完善的管理制度和产品开发流程,但是依然认为竞争对手是那样强大,那样不可战胜。

OSG学习:LOD、数据分页、动态调度

LOD(level of detail):是指根据物体模型的结点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。在OSG的场景结点组织结构中,专门提供了场景结点osg::LOD来表达不同的细节层次模型。其中,osg::LOD结点作为父节点,每个子节点作为一个细节层次,设置不同的视域,在不同的视域下显示相应的子节点。 数据分页:在城市

代码随想录——摆动序列(Leetcode376)

题目链接 贪心 class Solution {public int wiggleMaxLength(int[] nums) {if(nums.length <= 1){return nums.length;}// 当前一对差值int cur = 0;// 前一对差值int pre = 0;// 峰值个数int res = 1;for(int i = 0; i < nums.length -

想让Python序列切片更高效?这些技巧你不可不知!

目录 1、自定义类实现切片 🍏 1.1 实现__getitem__方法 1.2 支持正负索引与步长 2、利用 collections.abc 模块 🧠 2.1 继承MutableSequence类 2.2 重写关键方法 3、使用标准库itertools.slice 🍲 3.1 itertools工具介绍 3.2 slice函数应用实例 4、通过生成器实现动态切片 🌀

Java代理-动态字节码生成代理的5种方式

上篇讲到了代理模式出现的原因,实现方式以及跟其他相似设计模式的区别。传送门@_@ http://blog.csdn.net/wonking666/article/details/79497547 1.静态代理的不足 设计模式里面的代理模式,代理类是需要手动去写的。但是手写代理的问题颇多 1.如果不同类型的目标对象需要执行同样一套代理的逻辑,比如说在方法调用前后打印参数和结果,那么仍然需要为每

关于修改计算机的处理器数和最大内存数的问题

问题描述: 刚开始本来是想让计算机的运行速度运行的快点,于是在网上搜索如何让计算机的运行速度更快,找到了一种关于修改计算机内存数和计算机的处理核数可以让计算机运行的更快。 遇到问题: 当我通过命令msconfig →引导→高级选项→勾选了处理器数和最大内存数,然后重启,结构整个计算机都卡的要死,于是记录下来。网上的答案有时候真的是很不负责任,也有可能是自己技术不到位。 结果:取消处理器和内