程序设计思维与实践 Week5 作业

A - 最大矩形

在这里插入图片描述

Input

输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。

Output

对于每组测试数据输出一行一个整数表示答案。

Sample Input

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

Sample Output

8
4000

解题思路

1、单调栈
2、如果确定了一个矩形的左端点为l,右端点为r,那么矩形的高怎么确定?
• 根据题意,高度不能超过l到r中的最小值
• 要是整个矩形最大,那么高度就确定为l到r中的最小值
• 同理,如果确定了矩形的高度,那么左端点一定是越靠左,右端点越靠右,这个矩形的面积才可能最大
• 左端点可以确定为往左数第一个小于此高度的点
• 右端点可以确定为往右数第一个小于此高度的点
• 两遍单调栈处理出以每个点为高时的左右端点
3、单调递减栈,从左到右遍历,寻找右端点。
栈中存储的是各个矩形的编号。如果栈顶元素为空或者栈顶元素所表示的矩形的高小于入栈元素所表示的矩形的高,则入栈,如多不满足条件,则要将栈顶的元素弹出。
一旦发现第i个元素所表示的矩形的高比栈顶元素所表示的矩形的高小,则说明找到了第一个比栈顶元素所表示的高小的矩形所在编号,即找到了以栈顶元素所表示的小矩形为高的右端点。
同理用单调递减栈从右往左遍历找到左端点,最后算出面积。
4、时间复杂度O(n)

代码

#include<iostream>
#include<stack>
using namespace std;
int main()
{
 int n;
 while (cin >> n, n != 0)
 {
  long long* a = new long long[n];
  long long* r = new long long[n];//存储矩形的右端点 
  long long* l = new long long[n];//存储矩形的左端点 
  for (int i = 0; i < n; i++)
   cin >> a[i];
  stack<int> s1;
  stack<int> s2;
  long long ans = 0;
  long long max = 0;
  for (int i = 0; i < n; i++)
  {
   while (s1.size() > 0 && a[s1.top()] > a[i])
   {
    r[s1.top()] = i - 1;
    //cout << r[s1.top()] << " " << s1.top() << endl;
    s1.pop();
   }
   s1.push(i);
  }
  while (s1.size() > 0)
  {
   r[s1.top()] = n-1;
   s1.pop();
  }
  for (int i = n-1; i >=0; i--)
  {
   while (s2.size() > 0 && a[s2.top()] > a[i])
   {
    l[s2.top()] = i+1;
    s2.pop();
   }
   s2.push(i);
  }
  while (s2.size() > 0)
  {
   l[s2.top()] = 0;
   s2.pop();
  }
  for (int i = 0; i < n; i++)
  {
   ans = a[i] * (r[i] - l[i] + 1);
   max = max > ans ? max : ans;
  }
  cout << max << endl;
  delete[]a;
  delete[]r;
  delete[]l;
 }
}
  

B - TT’s Magic Cat

Thanks to everyone’s help last week, TT finally got a cute cat. But what TT didn’t expect is that this is a magic cat.
One day, the magic cat decided to investigate TT’s ability by giving a problem to him. That is select nn cities from the world map, and a[i]a[i] represents the asset value owned by the i-th city.
Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r] and increase their asset value by cc. And finally, it is required to give the asset value of each city after qq operations.
Could you help TT find the answer?

Input

The first line contains two integers n,q (1≤n,q≤2⋅105)(1≤n,q≤2·105) --the number of cities and operations.
The second line contains elements of the sequence aa: integer numbers a1,a2,…,an (−106≤ai≤106)(−106≤ai≤106).
Then qq lines follow, each line represents an operation. The ii-th line contains three integers l,rl,r and cc (1≤l≤r≤n,−105≤c≤105)(1≤l≤r≤n,−105≤c≤105) for the i-th operation.

Output

Print n integers a1,a2,…,ana1,a2,…,an one per line, and aiai should be equal to the final asset value of the ii-th city.

Output

Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the ii-th city.

在这里插入图片描述

解题思路

  • 差分法

  • 差分构造方式
    原数组A,差分数组B,数组范围[1,n]
    B[1]=A[1]
    B[i]=A[i]-A[i-1]

  • 差分特点

    B 数组前缀和 ⇔ A 数组元素值
    SUM{B[1~i]} = A[i]
    A 数组的区间加 ⇔ B 数组的单点修改
    A[L]~A[R] 均加上 c 等价于 B[L] += c, B[R+1] -= c

  • 将原数组转变成差分数组,将区间修改转变为单点修改

  • A[L,R]+=c<=>B[L]+=c,B[R+1]-=c

  • B数组前缀和即为A数组最终数值

  • 时间复杂度o(n+q)

代码

#include<iostream>
using namespace std;
int main()
{
int n, q;
 cin >> n >> q;
 long long* a = new long long[n+1];
 long long* b = new long long[n+1];
 for (int i = 1; i <= n; i++)
  cin >> a[i];
  b[1] = a[1];
 for (int i = 2; i <= n; i++)
  b[i] = a[i] - a[i - 1];
  for (int i = 0; i < q; i++)
 {
  int l, k,c;
  cin >> l >> k >> c;
  b[l] += c;
  if(k<n)
   b[k + 1] -= c;
 }
 long long sum=0;
 for (int i = 1; i <= n; i++)
 {
  sum += b[i];
  cout << sum << " ";
 }
 delete[]a;
 delete[]b;
}

C - 平衡字符串

一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。

Input

一行字符表示给定的字符串s

Output

一个整数表示答案

Examples

Input

QWER

Output

0

Input

QQWE

Output

1

Input

QQQW

Output

2

Input

QQQQ

Output

3

解题思路

尺取法

  • 又称双指针法,即在遍历数组的过程种,用两个相同方向进行扫描。

什么情况下能使用尺取法

  • 所求解答案为一个连续区间
  • 区间左右端点有明确移动方向。

是否可以用尺取法?

  • 所求解为一个连续区间
  • 区间左右端点移动有明确方向
    当前[L,R]满足要求,则L++
    当前[L,R]不满足要求,则R++
    因此此题可以用尺取法

怎么判断当前[L,R]是否满足要求?

  • 用Q,W,E,R分别记录下不包含区间[L,R]这一字段时,字符’Q’,‘W’,‘E’,'R’的个数

  • 先通过替换使4类字符数量一致,再判断剩余空闲位置是否是4的倍数

  • MAX = max(sum1, sum2, sum3, sum4)

  • TOTAL = R – L + 1 • FREE = TOTAL -[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)]

  • 若 FREE ≥ 0 且为 4 的倍数,则满足要求;否则不满足

代码

#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
 string s;
 cin >> s;
 int Q=0, W=0, E=0, R=0;//记录Q,W,E,R的数量
 for (int i = 0; i < s.length(); i++)
 {
  if (s[i] == 'Q')
   Q++;
  else if (s[i] == 'W')
   W++;
  else if (s[i] == 'E')
   E++;
  else
   R++;
 }
 int ans=s.length();
 if (Q == W == E == R)
 {
  ans = 0;
  cout << ans << endl;
  return 0;
 }
 int l = 0, r = 0;
 for (int i = l; i <= r; i++)
 {
  if (s[i] == 'Q')
   Q--;
  else if (s[i] == 'W')
   W--;
  else if (s[i] == 'E')
   E--;
  else
   R--;
 }
 while (l < s.length() && r < s.length())
 {
  int mmax = max(max(Q, W), max(E, R));
  int len = r - l + 1;
  len -= (mmax - Q) + (mmax - W) + (mmax - E) + (mmax - R);
  if (len >= 0 && len % 4 == 0)
  {
   ans = min(ans, r - l + 1);
   if (s[l] == 'Q')
    Q++;
   else if (s[l] == 'W')
    W++;
   else if (s[l] == 'E')
    E++;
   else
    R++;
   l++;
  }
  else
  {
   r++;
   if (s[r] == 'Q')
    Q--;
   else if (s[r] == 'W')
    W--;
   else if (s[r] == 'E')
    E--;
   else
    R--;
  }
  }
  cout << ans << endl;
  return 0;
}

D - 滑动窗口

在这里插入图片描述

Input

   输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。

Output

输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

1 -3 -3 -3 3 3
3 3 5 5 6 7

解题思路

  • 单调队列
  • 求窗口内最小值使用单调递增队列,每次返回队首元素
  • 求窗口内最大值使用单调递减队列,每次返回队首元素
  • 从左向右移动,每次半段下一个元素。在移动过程中需要判断队首元素是否在窗口内。若q.back()-q.front()+1<k则说明在。否则不在,弹出队首元素。

代码

#include<iostream>
#include<deque>
#include<stdio.h>
using namespace std;
int main()
{
 int n, k;
 scanf("%d %d", &n, &k);
 int* a = new int[n];
 int* mmin = new int[n];
 int* mmax = new int[n];
for (int i = 0; i < n; i++)
  scanf("%d", &a[i]);
 deque<int> q1, q2;
 int m = 0, j = 0;
 for (int i = 0; i < k; i++)
 {
  while (!q1.empty() && a[q1.back()] > a[i])
   q1.pop_back();
  q1.push_back(i);
 }
 mmin[m++] = a[q1.front()];
 for (int i = k; i < n; i++)
 {
  while (!q1.empty() && a[q1.back()] > a[i])
   q1.pop_back();
  q1.push_back(i);
  //cout<<q1.front()<<endl;
  while (!q1.empty() && q1.back() - q1.front() + 1 > k)
   q1.pop_front();
  mmin[m++] = a[q1.front()];
 }
 for (int i = k; i < n; i++)
 {
  while (!q1.empty() && a[q1.back()] > a[i])
   q1.pop_back();
  q1.push_back(i);
  //cout<<q1.front()<<endl;
  while (!q1.empty() && q1.back() - q1.front() + 1 > k)
   q1.pop_front();
  mmin[m++] = a[q1.front()];
 }
 for (int i = 0; i < k; i++)
 {
  while (!q2.empty() && a[q2.back()] < a[i])
   q2.pop_back();
  q2.push_back(i);
 }
 mmax[j++] = a[q2.front()];
for (int i = k; i < n; i++)
 {
  while (!q2.empty() && a[q2.back()] < a[i])
   q2.pop_back();
  q2.push_back(i);
  while (!q2.empty() && q2.back() - q2.front() + 1 > k)
   q2.pop_front();
  mmax[j++] = a[q2.front()];
 }
 for (int i = 0; i < n - k; i++)
  printf("%d ", mmin[i]);
 printf("%d\n", mmin[n - k]);
 for (int i = 0; i < n - k; i++)
  printf("%d ", mmax[i]);
 printf("%d\n", mmax[n - k]);
 delete[]a;
 delete[]mmin;
 delete[]mmax;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值