您的位置:首页 > 理论基础 > 数据结构算法

(复习)数据结构--单调栈--新知训练 (POJ2796,POJ2559,POJ3494,BZOJ1012)

2016-08-31 21:03 309 查看

POJ-2796 Feel Good

题意:

给出一个长度为n(1<=n<=100 000)的序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大。

输入格式:

输入数据有2行,第一行正整数n,第2行n个正整数ai。(0<=ai<=10^6)

输出格式:

输出有2行,第一行为题目描述中的最大值,第二行2个数字,分别为子序列的起点和终点位置。

样例输入:

6

3 1 6 4 5 2

样例输出:

60

3 5

思路:

我们可以把一段可能作为答案的区间理解为第某个元素为最小值的极大区间,也就是说,只要确定了最小值,再通过最小值扩展出极大的可用区间就可以了。

既然已知该数是区间内的最小值,则该数字到区间的最右端一定是单调递增序列,且该区间的最左端到该数字一定是单调递减序列,由此可用单调栈求出每个元素所对应的区间,时间复杂度O(n)。

代码:

/*
2016.8.31 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int n;
int a[100005];
int leftt[100005];
int rightt[100005];
int s[100005];
int top=0;
long long sum[100005];
long long ans=0;
int pos=0;
int main()
{
freopen("good.in","r",stdin);
freopen("good.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
a[0]=a[n+1]=-1;
for(int i=1;i<=n+1;i++)
{
while(top && a[i]<a[s[top]]) leftt[s[top--]]=i-1;
s[++top]=i;
}
memset(s,0,sizeof s);
top=0;
for(int i=n;i>=0;i--)
{
while(top && a[i]<a[s[top]]) rightt[s[top--]]=i+1;
s[++top]=i;
}
for(int i=1;i<=n;i++)
{
long long bulabula=sum[leftt[i]]-sum[rightt[i]-1];
if(ans<bulabula*a[i])
{
ans=bulabula*a[i];
pos=i;
}
}
printf("%lld\n%d %d",ans,rightt[pos],leftt[pos]);
fclose(stdin);
fclose(stdout);
return 0;
}


POJ-2559 Largest Rectangle in a Histogram

题意:

给出一个柱形统计图(histogram), 它的每个项目的宽度是1, 高度和具体问题有关。 现在编程求出在这个柱形图中的最大面积的长方形.

例如下图:2, 1, 4, 5, 1, 3, 3 。 那么最大面积:8



输入格式:

输入有若干组数据,每组数据开始是一个整数n(1<=n<=100000),表示柱形图的个数,后面有n个整数表示每个柱形图的高hi(0<=hi<=1000 000 000)。输入以0结束。

输出格式:

输出若干行,每组数据输出最大面积的值。

样例输入:

7 2 1 4 5 1 3 3

4 1000 1000 1000 1000

0

样例输出:

8

4000

思路:

如短板原理一般,一个矩形的最大高度取决于最低的那个小矩形的高度,如上一题用单调栈处理出每一个长条可以向左向右扩展的广度,扫描并计算更新答案即可。

代码:

/*
2016.8.31 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
bool b[2005][2005];
int a[2005][2005];
int s[2005];
int leftt[2005];
int rightt[2005];
int top=0;
int ans=0;
int solve(int a[])
{
a[0]=a[m+1]=-1;
memset(s,0,sizeof s);
memset(leftt,0,sizeof leftt);
memset(rightt,0,sizeof rightt);
top=0;
for(int i=1;i<=m+1;i++)
{
while(top && a[s[top]]>a[i]) rightt[s[top--]]=i-1;
s[++top]=i;
}
memset(s,0,sizeof s);
top=0;
for(int i=m;i>=0;i--)
{
while(top && a[s[top]]>a[i]) leftt[s[top--]]=i+1;
s[++top]=i;
}
int bulabula=0;
for(int i=1;i<=m;i++)
{
bulabula=max(bulabula,(rightt[i]-leftt[i]+1)*a[i]);
}
return bulabula;
}
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
while(scanf("%d%d",&n,&m)==2)
{
memset(a,0,sizeof a);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&b[i][j]);
if(b[i][j]) a[i][j]=a[i-1][j]+1;
}
ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,solve(a[i]));
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}


POJ-3494 Largest Submatrix of All 1’s

题目大意:

给出一个m*n的01矩阵,求由1组成的最大矩形,最大矩形是指包含元素1的个数最多。

输入格式:输入包含多组数据,每组数据第一行是m和n(1 ≤ m, n ≤ 2000) 两个整数,接下来是m行n列由0和1组成的矩阵。

输出格式:

对于每组数据输出一最大矩形的1的个数,如果输入的矩阵都是0,则输出0。

输入样例:

2 2

0 0

0 0

4 4

0 0 0 0

0 1 1 0

0 1 1 0

0 0 0 0

输出样例:

0

4

思路:

枚举小矩形的底边既行,同上一题,求出以该行为底边的矩形的最大的面积,就可以枚举到每一个矩形。求最大面积时同上一题。

代码:

/*
2016.8.31 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
bool b[2005][2005];
int a[2005][2005];
int s[2005];
int leftt[2005];
int rightt[2005];
int top=0;
int ans=0;
int solve(int a[])
{
a[0]=a[m+1]=-1;
memset(s,0,sizeof s);
memset(leftt,0,sizeof leftt);
memset(rightt,0,sizeof rightt);
top=0;
for(int i=1;i<=m+1;i++)
{
while(top && a[s[top]]>a[i]) rightt[s[top--]]=i-1;
s[++top]=i;
}
memset(s,0,sizeof s);
top=0;
for(int i=m;i>=0;i--)
{
while(top && a[s[top]]>a[i]) leftt[s[top--]]=i+1;
s[++top]=i;
}
int bulabula=0;
for(int i=1;i<=m;i++)
{
bulabula=max(bulabula,(rightt[i]-leftt[i]+1)*a[i]);
}
return bulabula;
}
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
while(scanf("%d%d",&n,&m)==2)
{
memset(a,0,sizeof a);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&b[i][j]);
if(b[i][j]) a[i][j]=a[i-1][j]+1;
}
ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,solve(a[i]));
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}


BZOJ-1012 [JSOI2008]最大数Maxnumber

题目描述

现在请求你维护一个数列,要求提供以下两种操作: 1、 查询操作。语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。 2、 插入操作。语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。注意:初始时数列是空的,没有一个数。

输入格式

第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足1≤int64max

输出格式

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

样例输入

5 100

A 96

Q 1

A 97

Q 1

Q 2

样例输出

96

93

96

思路:

这道题一眼线段树,但是经过思考应该会有更好的算法因为每一次查询的区间不管k是多少,都是[k,len]。也就是说每一次查询的边界都是最后一个数字,所以说某个数后面的数,若大于之前所有的数,那么之前的数不可能成为最大值的。我们就可以维护一个单调递减的栈,栈中元素存储该数字的位置。每一次查询的时候查找位置大于k的第一个元素。

代码:

/*
2016.8.31 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
long long moder;
int n,m;
long long a[200005];
int s[200005],top=0;
long long t=0;
int main()
{
freopen("maxnumber.in","r",stdin);
freopen("maxnumber.out","w",stdout);
scanf("%d%I64d",&m,&moder);
for(int k=1;k<=m;k++)
{
char c[5];
long long x;
scanf("%s%I64d",c,&x);
if(c[0]=='Q') //二分查找
{
int pos=n-x+1;
int l=1,r=top;
int mid=(l+r)/2;
while(l<=r)
{
mid=(l+r)/2;
if(s[mid]==pos) break;
else if(s[mid]<pos) l=mid+1;
else r=mid-1;
}
if(s[mid]<pos) mid++;
t=a[s[mid]];
printf("%I64d\n",a[s[mid]]);
}
else if(c[0]=='A')
{
x+=t;
x%=moder;
a[++n]=x;
while(top && a[s[top]]<=a
) top--;
s[++top]=n;
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj