2019年5月1日

总结

按照进度,我们今天来看单调队列。
首先,什么是单调队列。
单调队列:队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。
演示

一组数(1,3,2,1,5,6),进入单调不减队列的过程
1入队,得到队列(1);
3入队,得到队列(1,3);
2入队,这时,队尾的的元素3>2,将3从队尾弹出,新的队尾元素1<2,不用弹出,将2入队,得到队列(1,2);
1入队,2>1,将2从队尾弹出,得到队列(1,1);
5入队,得到队列(1,1,5);
6入队,得到队列(1,1,5,6);

例题

给定一个长度为n的数列,求长度为k的定长连续子区间{a1,a2,a3,a4,…,ak-1,ak}{a2,a3,…,ak,ak+1}……中每个区间的最大值和最小值。

我们将区间从(l,r)移动到(l+1,r+1)时,我们将ar+1插入单调队列,若队首元素不在(l,r)区间当中,删除它。

int a[M],Q[M],I[M]; 
int i,n,k; 
void getMin(){ 
    int head=1,tail=0; 
    for(i=1;i<k;i++){ 
        while(head<=tail&&Q[tail]>=a[i])tail--; 
        tail++; 
        Q[tail]=a[i];I[tail]=i; 
    } 
    for(i=k;i<=n;i++){ 
        while(head<=tail&&Q[tail]>=a[i])tail--; 
        tail++; 
        Q[tail]=a[i];I[tail]=i; 
        while(I[head]<=i-k) head++; 
        printf("%d ",Q[head]); 
    } 
} 
void getMax(){ 
    int head=1,tail=0; 
    for(i=1;i<k;i++){ 
        while(head<=tail&&Q[tail]<=a[i])tail--; 
        tail++; 
        Q[tail]=i; 
    } 
    for(i=k;i<=n;i++){ 
        while(head<=tail&&a[Q[tail]]<=a[i])tail--; 
        tail++; 
        Q[tail]=i; 
        while(Q[head]<=i-k)head++; 
        printf("%d ",a[Q[head]]); 
    } 
} 
int main(){ 
    scanf("%d %d",&n,&k); 
    for(i=1;i<=n;i++) 
        scanf("%d",&a[i]); 
    getMin(); 
    printf("\n"); 
    getMax(); 
    return 0; 
}

印刷广告,广告牌是刷在城市的建筑物上的,城市里有紧靠着的N(N<=400000)个建筑。afy决定在上面找一块尽可能大的矩形放置广告牌。我们假设每个建筑物都有一个高度,从左到右给出每个建筑物的高度H1,H2…HN,0<Hi<=1,000,000,000,并且我们假设每个建筑物的宽度均为1。要求输出广告牌的最大面积。
define MAXN 1000000
int h[MAXN+5];  //建筑物的高度
int n;   //建筑物的数目
int mq[MAXN+5];  //单调队列,对内元素为建筑物高度的下标
int left[MAXN+5]; //left[i]:在第i个建筑物左侧,不比它的高度小的建筑物数量
int right[MAXN+5]; //right[i]:在第i个建筑物右侧,不比它的高度小的建筑物数量
void CalcLeft()
{
 mq[0] = 0;
 int front = 0, rear = 1;
 
 int i;
 for (i = 1; i <= n; i++)
 {
  while (front < rear && h[i] <= h[mq[rear-1]])
  {
   rear--;
  }
  left[i] = i - mq[rear-1] - 1;
  mq[rear++] = i;
 }
}
void CalcRight()
{
 mq[0] = n + 1;
 int front = 0, rear = 1;
int i;
 for (i = n; i >= 1; i--)
 {
  while (front < rear && h[i] <= h[mq[rear-1]])
  {
   rear--;
  }
  right[i] = mq[rear-1] - i - 1;
  mq[rear++] = i;
 }
}
int MaxRectArea()
{
 int maxArea = -1;
 int i;
 for (i = 1; i <= n; i++)
 {
  int area = (left[i] + right[i] + 1) * h[i];
  if (area > maxArea)
  {
   maxArea = area;
  }
 }
 return maxArea;
}
int main()
{
 while (cin >> n)
 {
  int i;
  for (i = 1; i <= n; i++)
  {
   cin >> h[i];
  }
  h[0] = h[n+1] = -1;
  CalcLeft();
  CalcRight();
  for (i = 1; i <= n; i++)
   {
 cout << left[i] << ' ';
   }
cout << endl;
for (i = 1; i <= n; i++)
{
 cout << right[i] << ' ';
}
cout << endl;
cout << MaxRectArea() << endl;
}
 return 0;
}

感慨

今天忙了一天了。
心神俱疲。
累死了。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值