热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

单调队列、单调栈(第一周DIY)

LargestRectangleinaHistogramTimeLimit:20001000MS(JavaOthers)MemoryLimit:6553632768

Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4087    Accepted Submission(s): 1218


Problem Description A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
 
Input The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1 <= n <= 100000. Then follow n integers h1, ..., hn, where 0 <= hi <= 1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.  
Output For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.  
Sample Input
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
 
Sample Output
8
4000

 

http://acm.hdu.edu.cn/showproblem.php?pid=1506

方法一:

思路:对于每一点(设高度为h),向左找到第一个比它小的数(设这个下标为i),向右找到第一个比它小的数(设这个下标为j),那么此时以这点为高度的最大
   面积为area=(i-j+1)*h; 以此类推,用for循环遍历数组,对每一点求面积,可求出最大的area.

关键:解题重点在于求左边和右边的“边界”,如果按常规算法,肯定会TLE,因此需用到动态规划。

#include
#include
#include
using namespace std;
#define MAXN 100010
__int64 rec[MAXN], l[MAXN],r[MAXN],max_area,sum;

int main()
{
freopen("input.txt","r",stdin);
int n,i;
while(scanf("%d",&n),n){

for(i=1; i<=n; ++i){
scanf("%I64d",&rec[i]);
l[i]=r[i]=i;
}

rec[0]=rec[n+1]=-1;

for(i=1; i<=n; ++i){
while(rec[i] <= rec[l[i]-1])
l[i] = l[l[i]-1];
}
for(i=n; i>=1; --i){
while(rec[i] <= rec[r[i]+1])
r[i] = r[r[i]+1];
}



max_area = -2147483647;
for(i=1; i<=n; ++i){
sum = rec[i]*(r[i]-l[i]+1);
if(sum > max_area)
max_area = sum;
}

printf("%I64d\n",max_area);
}
return 0;
}


方法二(单调栈):

使用栈线性扫描解决该问题
对于一个新的元素:
(1)如果此时栈为空或者栈顶元素比新元素小,则将该元素入栈;
(2)如果栈顶元素与新元素相等,则跳过新元素;
(3)如果栈顶元素比新元素大,那么此时需要更新栈顶元素并更新面积,一直到栈顶元素小于新元素为止。

#include
#include
#include
using namespace std;

__int64 rec[100010], max_area,sum;

int main()
{
//freopen("input.txt","r",stdin);
int n,i,temp;
while(scanf("%d",&n),n){

for(i=0; i scanf("%I64d",&rec[i]);

max_area = -2147483647; //注意: 不能给__int64或long long直接赋值-2147483648,否则会变成2147483648. 为什么?

rec[n]=-1; //最后一个元素的下一个赋值为-1, 则这个一定会比栈顶元素小
stackq;

for(i=0; i<=n; ++i){

if(q.empty() || rec[i]>rec[q.top()]){
q.push(i);
}
else if(rec[i] while(!q.empty() && rec[q.top()]>rec[i]){
sum = (i-q.top())*rec[q.top()];
if(sum>max_area)
max_area = sum;
temp = q.top(); //记录跳出盏的元素的下标。
q.pop();
}
q.push(temp); //此时temp下标表示以rec[i]为高度的矩形的左边的边界(因为盏是递减的,所以左边的比右边的高)
rec[temp] = rec[i]; //这里注意理解,把rec[i]的值赋值给rec[temp](原来的高度已经没用了),此时它表示的是高为rec[i]的矩形
}

}
printf("%I64d\n",max_area);
}
return 0;
}


 

    Max Sum of Max-K-sub-sequence

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 46   Accepted Submission(s) : 4
Problem Description Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence means the left neighbour of A[1] is A[n] , and the right neighbour of A[n] is A[1].
Now your job is to calculate the max sum of a Max-K-sub-sequence. Max-K-sub-sequence means a continuous non-empty sub-sequence which length not exceed K.
 


 

Input The first line of the input contains an integer T(1<=T<=100) which means the number of test cases.
Then T lines follow, each line starts with two integers N , K(1<=N<=100000 , 1<=K<=N), then N integers followed(all the integers are between -1000 and 1000).
 


 

Output For each test case, you should output a line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the minimum start position, if still more than one , output the minimum length of them.  


 

Sample Input
4
6 3
6 -1 2 -6 5 -5
6 4
6 -1 2 -6 5 -5
6 3
-1 2 -6 5 -5 6
6 6
-1 -1 -1 -1 -1 -1
 


 

Sample Output
7 1 3
7 1 3
7 6 2
-1 1 1

 

题目大意:给出一个有N个数字(-1000..1000,N<=10^5)的环状序列,让你求一个和最大的连续子序列。这个连续子序列的长度小于等于K。  

分析:因为序列是环状的,所以可以在序列后面复制一段(或者复制前k个数字)。如果用s[i]来表示复制过后的序列的前i个数的和,那么任意一个子序列[i..j]的和就等于s[j]-s[i-1]。对于每一个j,用s[j]减去最小的一个s[i](i>=j-k+1)就可以得到以j为终点长度不大于k的和最大的序列了。将原问题转化为这样一个问题后,就可以用单调队列解决了。


 

单调队列即保持队列中的元素单调递增(或递减)的这样一个队列,可以从两头删除,只能从队尾插入。单调队列的具体作用在于,由于保持队列中的元素满足单调性,对于上述问题中的每个j,可以用O(1)的时间找到对应的s[i]。(保持队列中的元素单调增的话,队首元素便是所要的元素了)。

 

维护方法:对于每个j,我们插入s[j-1](为什么不是s[j]? 队列里面维护的是区间开始的下标,j是区间结束的下标),插入时从队尾插入。为了保证队列的单调性,我们从队尾开始删除元素,直到队尾元素比当前需要插入的元素优(本题中是值比待插入元素小,位置比待插入元素靠前,不过后面这一个条件可以不考虑),就将当前元素插入到队尾。之所以可以将之前的队列尾部元素全部删除,是因为它们已经不可能成为最优的元素了,因为当前要插入的元素位置比它们靠前,值比它们小。我们要找的,是满足(i>=j-k+1)的i中最小的s[i],位置越大越可能成为后面的j的最优s[i]。

在插入元素后,从队首开始,将不符合限制条件(i>=j-k+1)的元素全部删除,此时队列一定不为空。(因为刚刚插入了一个一定符合条件的元素)

 

#include
#include
#include
using namespace std;

int arr[200010],sum[200010];

int main()
{
// freopen("input.txt","r",stdin);
int T;
int N,K,i;
scanf("%d",&T);
while(T--){
scanf("%d %d",&N,&K);
sum[0] = 0;
for(i=1; i<=N; ++i){
scanf("%d",&arr[i]);
sum[i] = sum[i-1] + arr[i];
}
for(i=N+1; i sum[i] = sum[i-1] + arr[i-N];
}
dequeq;
q.clear();
int max = -2147483647,start,end;
for(i=1; i
while(!q.empty() && sum[q.back()] > sum[i-1])
q.pop_back ();

while(!q.empty() && q.front() <(i-K))
q.pop_front();

q.push_back (i-1);

int val = sum[i] - sum[q.front()];
if(val > max){
max = val;
start = q.front() + 1;
end = i;
}
}
printf("%d %d %d\n",max, start, end>N?end%N:end);
}
return 0;
}


 

 

这一题中,用到了一个技巧,即对于一个序列 1,2,3,4,5,6,7,…… 要求某一区间的和, 例如第i数到第j个数的和,那么可以用前j个数的和减去前i-1个数的和

这个技巧可以拓展到矩阵,看下一题:

                                                        最大子矩阵
Time Limit : 30000/10000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 7   Accepted Submission(s) : 4
Problem Description 给你一个m×n的整数矩阵,在上面找一个x×y的子矩阵,使子矩阵中所有元素的和最大。  


 

Input 输入数据的第一行为一个正整数T,表示有T组测试数据。每一组测试数据的第一行为四个正整数m,n,x,y(0  


 

Output 对于每组数据,输出一个整数,表示子矩阵的最大和。  


 

Sample Input
1
4 5 2 2
3 361 649 676 588
992 762 156 993 169
662 34 638 89 543
525 165 254 809 280
 


 

Sample Output
2474


 http://acm.hdu.edu.cn/showproblem.php?pid=1559

 

对于一个n行的矩阵,可以把n行看成一行,即和并列,每一列看成一个元素;以此推广。

#include
#include
#include
using namespace std;

int arr[200010],sum[200010];

int main()
{
// freopen("input.txt","r",stdin);
int T;
int N,K,i;
scanf("%d",&T);
while(T--){
scanf("%d %d",&N,&K);
sum[0] = 0;
for(i=1; i<=N; ++i){
scanf("%d",&arr[i]);
sum[i] = sum[i-1] + arr[i];
}
for(i=N+1; i sum[i] = sum[i-1] + arr[i-N];
}
dequeq;
q.clear();
int max = -2147483647,start,end;
for(i=1; i
while(!q.empty() && sum[q.back()] > sum[i-1])
q.pop_back ();

while(!q.empty() && q.front() <(i-K))
q.pop_front();

q.push_back (i-1);

int val = sum[i] - sum[q.front()];
if(val > max){
max = val;
start = q.front() + 1;
end = i;
}
}
printf("%d %d %d\n",max, start, end>N?end%N:end);
}
return 0;
}


 


——      生命的意义,在于赋予它意义。 

                   原创 http://blog.csdn.net/shuangde800 , By   D_Double





推荐阅读
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文详细介绍了解决全栈跨域问题的方法及步骤,包括添加权限、设置Access-Control-Allow-Origin、白名单等。通过这些操作,可以实现在不同服务器上的数据访问,并解决后台报错问题。同时,还提供了解决second页面访问数据的方法。 ... [详细]
author-avatar
cc_vb8
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有