A - 最大矩形
题目
样例输入
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
样例输出
8
4000
思路
以单调递增栈来说,就是从栈顶到栈底元素单调递增,元素入栈时如果比栈顶元素大,则将栈顶元素出栈直至栈顶元素比入栈元素大。如果将一个数组依次入栈,对于由于元素入栈而被迫出栈的元素来讲,入栈元素就是第一个比他们大的元素。单调递减栈同理。
模拟单调递增栈入栈过程:
[10] 10 入栈时, 栈空, 入栈
[3, 10] 3 入栈时, 栈顶元素 10 大于 3, 入栈
[7, 10] 7入栈时, 栈顶元素 3 小于 7, 弹栈; 栈顶元素 10 大于 7, 入栈
[4, 7 ,10] 4 入栈时, 栈顶元素 7 大于 4, 入栈
[12] 12 入栈时, 栈顶元素 4 小于 12, 弹栈; 栈顶元素 7 小于 12, 弹栈; 栈顶元素 10 小于 12, 弹栈; 栈空, 入栈
这道题需要算出每个高度不同矩形的最大面积。假设l是其中一个高度为h的矩形的左边界,r是右边界。那么对于这个矩形来讲,l是左边第一个低于h的坐标,r是右边第一个低于h的坐标。对于l,r可通过单调递减栈求解(数组正向求解一次,反向求解一次)。
代码
#include <iostream>
#include<stack>
using namespace std;
struct s
{
int l;
int r;
long long h;
};
int main()
{
int n;
while (1)
{
cin >> n;
if (n == 0) break;
long long *a = new long long[n];
long long *b = new long long[n];
struct s *c = new s[n];
for (int i = 0; i < n; i++)
{
cin >> a[i];
b[n - 1 - i] = a[i];
}
stack<pair<int, long long> > q;
for (int i = 0; i < n; i++)
{
while (!q.empty() && q.top().second > a[i])
{
c[q.top().first].r = i;
c[q.top().first].h = q.top().second;
q.pop();
}
pair<int, long long> p1(i, a[i]);
q.push(p1);
}
while (!q.empty())
{
c[q.top().first].r = n;
c[q.top().first].h = q.top().second;
q.pop();
}
for (int i = 0; i < n; i++)
{
while (!q.empty() && q.top().second > b[i])
{
c[n - 1 - q.top().first].l = n - 1 - i;
q.pop();
}
pair<int, long long> p1(i, b[i]);
q.push(p1);
}
while (!q.empty())
{
c[n - 1 - q.top().first].l = -1;
c[n - 1 - q.top().first].h = q.top().second;
q.pop();
}
long long max = 0;
for (int i = 0; i < n; i++)
{
long long ss = (c[i].r - c[i].l - 1) * c[i].h;
if (ss > max)
max = ss;
}
cout << max << endl;
}
}
B - TT's Magic Cat
题目
多亏了上周大家的帮助,它终于得到了一只可爱的猫。但没想到的是,这是一只神奇的猫。
有一天,神奇的猫决定调查TT的能力,给他一个问题。即从世界地图中选择n个城市,a[i]表示第i个城市拥有的资产价值。
然后,这只神奇的猫将执行几项操作。每轮选择[l,r]区间内的城市,并将其资产价值增加c,最后需要给出q操作后各城市的资产价值。
你能帮我找到答案吗?
输入
第一行包含两个整数n q(1≤n,q≤2e5)-城市数目和业务。
第二行包含序列a的元素:整数a1,a2,...,an(−1e6≤ai≤1e6).
接下来是q行,每一行代表一个操作。第i行包含三个整数l,r和c(1≤l≤r≤n,−1e5≤c≤1e5) ,对于第i次操作。
输出
打印n个整数a1,a2,…,an,每一行一个,i应该等于第i个城市的最终资产价值。
样例输入1
4 2
-3 6 8 4
4 4 -2
3 3 1
样例输出1
-3 6 9 2
样例输入2
2 1
5 -2
1 2 4
样例输出2
9 2
样例输入3
1 2
0
1 1 -8
1 1 -6
样例输出3
-14
思路
暴力解决的话时间复杂度为O(nq),但我们可以考虑使用差分数组来解决。
原数组为a,差分数组c(两个数组范围均为[1, n]),c[1] = a[1],c[i] = a[i] - a[i - 1];
按照这种构造方法,每次操作只需要c[l] += c, c[r + 1] -= c,将时间复杂度降到了O(n+q)。
代码
#include <iostream>
#include <cstring>
using namespace std;
long long m, n;
int main()
{
cin >> n >> m;
long long *a = new long long[n + 1];
long long *b = new long long[n + 1];
memset(a, 0, sizeof a);
memset(b, 0, sizeof b);
a[0] = 0;
for (long long i = 1; i <= n; i++)
{
cin >> a[i];
b[i] = a[i] - a[i - 1];
}
for (long long i = 0; i < m; i++)
{
long long l, r, value;
cin >> l >> r >> value;
b[l] += value;
if (r < n)
b[r + 1] -= value;
}
for (long long i = 1; i <= n; i++)
{
a[i] = b[i] + a[i - 1];
cout << b[i] + a[i - 1] << " ";
}
//system("Pause");
}
C - 平衡字符串
题目
思路
本道题采用尺取法。
所谓尺取法,在一个数组中维护两个指针 l 和 r ,起始时 l = r = 1(假设数组范围为[1, n]),
如果此时 [l, r] 中元素满足要求,l++;如果不满足要求,r++。
本题的要求就是使用 [l, r] 中的字符在将范围外的字符填充至数量相等时,剩余元素数为4的倍数。
MAX = max(sum1, sum2, sum3, sum4),sum表示个字符在范围外的总数
TOTAL = R – L + 1 ,范围内字符个数
FREE = TOTAL - [(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)],除去填充字符外的剩余字符。
分析化简可知,[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)] = 4 * MAX - N + TOTAL,N为字符串中的字符总数。
代码
#include <iostream>
#include <algorithm>
using namespace std;
char c[110000];
int main()
{
cin >> c;
int n = strlen(c);
int q = 0;
int w = 0;
int e = 0;
int r = 0;
for (int i = 0; i < n; i++)
{
if (c[i] == 'Q') q++;
if (c[i] == 'W') w++;
if (c[i] == 'E') e++;
if (c[i] == 'R') r++;
}
if (q == w && w == e && e == r) cout << 0 << endl;
else
{
int maxx = 0;
int l = 0;
int rr = 1;
int min = n;
//cout << "n:" << n << endl;
while (1)
{
int mm = rr - l;
int sub1 = 0;
int sub2 = 0;
int sub3 = 0;
int sub4 = 0;
for (int i = l; i < rr; i++)
{
if (c[i] == 'Q') sub1++;
if (c[i] == 'W') sub2++;
if (c[i] == 'E') sub3++;
if (c[i] == 'R') sub4++;
}
int maxx = max(max(q - sub1, w - sub2), max(e - sub3, r - sub4));
int total = 4 * maxx - n + mm;
int re = mm - total;
if (rr == n && (re < 0 || re % 4 != 0))
break;
if (re >= 0 && re % 4 == 0)
{
if (mm < min) min = mm;
l++;
}
else rr++;
}
cout << min << endl;
}
system("Pause");
}
D - 滑动窗口
题目
样例输入
8 3
1 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
思路
采用单调队列的方法,使用了deque双向队列。
队尾维护单调性,以单调递增队列举例,如果队尾元素出队列,直到队尾元素小于入队元素。
队首元素维护窗口的特性,每次入队的是数组的序号,如果序号小于左边界,则出队列,直到序号大于左边界。
那么队列中间的元素有可能序号小于左边界吗?
答案是否定的,因为在这种情况下,假设队列中序号小于左边界的元素为a[ i ],队首元素序号大于左边界,元素小于a[ i ]。事实上这种情况不可能出现,因为队首元素的序号大于i,即在 i 之后入队,而它的元素值又小于a[ i ],那么在它入队列时会将 i 挤出队列,因此这种情况不可能出现。
寻找最大元素维护一个递减队列,最小元素维护一个递增队列。
#include <iostream>
#include <deque>
#include <cstdio>
using namespace std;
deque<int> q;
int a[1000100];
int minn[1000100];
int maxx[1000100];
int main()
{
int n, k;
scanf_s("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf_s("%d", &a[i]);
for (int i = 0; i < k; i++)
{
while (!q.empty() && a[q.back()] >= a[i])
q.pop_back();
q.push_back(i);
}
minn[0] = a[q.front()];
for (int i = k; i < n; i++)
{
while (!q.empty() && q.front() <= (i - k))
q.pop_front();
while (!q.empty() && a[q.back()] >= a[i])
q.pop_back();
q.push_back(i);
minn[i - k + 1] = a[q.front()];
}
q.clear();
for (int i = 0; i < k; i++)
{
while (!q.empty() && a[q.back()] <= a[i])
q.pop_back();
q.push_back(i);
}
maxx[0] = a[q.front()];
for (int i = k; i < n; i++)
{
while (!q.empty() && q.front() <= (i - k))
q.pop_front();
while (!q.empty() && a[q.back()] <= a[i])
q.pop_back();
q.push_back(i);
maxx[i - k + 1] = a[q.front()];
}
for (int i = 0; i < n - k + 1; i++)
{
printf("%d", minn[i]);
printf(" ");
}
cout << endl;
for (int i = 0; i < n - k + 1; i++)
{
printf("%d", maxx[i]);
printf(" ");
}
cout << endl;
system("Pause");
}