week5作业

A - 最大矩形

题意

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。

Input

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

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

Input
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Output
8
4000

思路
在此题中,由于要求最大矩形的面积,如果确定了矩形的高度,那么左端点一定是越靠左,右端点越靠右,这个矩形的面积才会最大

左端点可以确定为往左数第一个小于此高度的点
右端点可以确定为往右数第一个小于此高度的点
利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置
采用递增栈的思想

从左向右遍历每个矩形,如果高度比栈顶高直接进栈。如果高度小于栈顶,则不断取出栈顶,直到栈顶高度比当前高度小,在出栈的过程中我们累加出栈矩形的宽度,每弹出一个矩形就用高度乘以宽度来记录该矩形的面积,并更新此时最大矩形面积。在整个过程结束后我们把一个宽度为当前累加的宽度,高度为当前宽度的矩形进栈。
易错点:注意数据范围,要使用long long,否则会溢出。

代码

#include <bits/stdc++.h>
using namespace std;
#define f(i,a,b) for(int i=(a);i<(b);++i)
const int maxn = 100005;
const int mod = 9973;
#define ll long long
ll a[maxn],L[maxn],R[maxn];
int main()
{
    int n;
    while(1)
    {
    	scanf("%d",&n);
		if(n==0) break;
        f(i,0,n)
        {
            scanf("%I64d",&a[i]);
        }
        stack<int> s;
        f(i,0,n)
        {
            while(s.size()&&a[s.top()]>=a[i])
                s.pop();
            if(s.empty())
                L[i]=0;
            else L[i]=s.top()+1;
            s.push(i);
        }
        
        while(s.size())
            s.pop();
            
        for(int i=n-1;i>=0;i--)
        {
            while(s.size()&&a[s.top()]>=a[i])
                s.pop();
            if(s.empty())
                R[i]=n;
            else R[i]=s.top();
            s.push(i);
        }
        
        ll ans=0;
        f(i,0,n)
        {   
            ll temp=a[i]*(R[i]-L[i]);
            if(temp>ans)
            ans=temp;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

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 n cities from the world map, and 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 c. And finally, it is required to give the asset value of each city after q operations.
Could you help TT find the answer?
输入说明

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 a: integer numbers a1,a2,…,an(−106≤ai≤106)a1​,a2​,…,an​(−106≤ai≤106).
Then q lines follow, each line represents an operation. The i-th line contains three integers l,r and c (1≤l≤r≤n,−105≤c≤105)

(1≤l≤r≤n,−105≤c≤105) for the i-th operation.
输出说明

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

思路
利用差分的思想。b[i]=a[i]-a[i-1]。此时如果想对a数组【l,r】区间里的每一个元素都加z。利用公式b[x]+=z;b[y+1]+= 0 - z;先将a数组插入b数组,此时x=y=i。再利用公式对每一个规定区间加z。最后根据ans+=b[i];输出原数组。注意:数据需要long long类型。
差分总结
差分构造方式

原数组 A,差分数组 B, 数组范围 [1, n]
B[1] = B[1]
B[i] = A[i] - A[i-1]

代码

#include<iostream>
using namespace std;
long long ans,a[300010],b[300010];
int n,q,x,y,z;
int main()
{
	scanf("%d%d", &n, &q);
	for(int i=1;i<=n;i++)
	{   
		scanf("%lld", &a[i]);
		
	}
	b[1]=a[1];
	for(int i=2;i<=n;i++)
			b[i]=a[i]-a[i-1];
			
	for(int i=1;i<=q;i++)
	{   scanf("%d%d%d", &x, &y, &z);
		b[x]+=z;
		b[y+1]+= 0 - z;
	}
	for(int i=1;i<=n;i++)
	{	
		ans+=b[i];
		printf("%lld ", ans);
	}
	return 0;
}

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=R=1,sum = a[L]
②当sum>=s时,符合要求,用(R-L+1)更新答案,且L++以寻找最小区间;若L=R,则L++,R++
③当sum<s时,不符合要求,R++,扩展区间以寻找满足条件的区间;
什么时候可以选用尺取法?
所求解答案为一个连续区间
区间左右端点移动有明确的方向
如果当前[L,R]满足要求,那么L++
如果当前[L,R]不满足要求,那么R++

先计算整个字符串中四个字符出现的频率,判断是否相等,若相等,则说明字符串已经平衡,直接输出“0”即可

若四个字符出现的频率不相等,则进行[L,R]区间的判断
①用s1,s2,s3,s4分别记录不同区间[L,R]这一段时,字符’Q’, ‘W’, ‘E’, 'R’的个数
②先通过替换时4类字符数量一致,再判断剩余空闲位置是否为4的倍数
③maxx=max(max(s1,s2),max(s3,s4));
④ total=r-l+1;
⑤free=tt-(maxx-s1)-(maxx-s2)-(maxx-s3)-(maxx-s4);
⑥若free>=0且为4的倍数,则满足要求,当前[L,R]满足要求,则L++,以寻求最小区间⑦不满足要求,则R++,继续扩展寻找合适区间

代码

#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
using namespace std;

bool judge(int s1,int s2,int s3,int s4,int tt)
{
	int maxx=max(max(s1,s2),max(s3,s4));
	int	free=tt-(maxx-s1)-(maxx-s2)-(maxx-s3)-(maxx-s4);
		if(free%4==0&&free>=0)
		return true;
	else return false;
}

int main()
{
	int s1=0,s2=0,s3=0,s4=0,count=0;
	string s;
	cin>>s;int n=s.size();
	for(int i=0;i<n;i++)
	{
		if(s[i]=='Q') s1++;
		if(s[i]=='W') s2++;
		if(s[i]=='E') s3++;
		if(s[i]=='R') s4++;
	}
	
	if(s1==s2&&s2==s3&&s3==s4)
	{
		cout<<0<<endl;
		return 0;
	}
	int ans=n+1,l=1,r=0,total=0,free=0;
	while(l<n &&r<=n)
	{   total=r-l+1;
	
		int k = judge(s1,s2,s3,s4,total);
		
		if(k)
		{
			ans=min(ans,total);
			if(s[l-1]=='Q') s1++;
			if(s[l-1]=='W') s2++;
			if(s[l-1]=='E') s3++;
			if(s[l-1]=='R') s4++;
			l++;	
		}
		else{
			if(s[r]=='Q') s1--;
			if(s[r]=='W') s2--;
			if(s[r]=='E') s3--;
			if(s[r]=='R') s4--;
			r++;
		}
		
	}
	cout<<ans<<endl;
	return 0;
}

D - 滑动窗口

题意
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
Window position
Minimum value
Maximum value
[1 3 -1] -3 5 3 6 7
-1
3
1 [3 -1 -3] 5 3 6 7
-3
3
1 3 [-1 -3 5] 3 6 7
-3
5
1 3 -1 [-3 5 3] 6 7
-3
5
1 3 -1 -3 [5 3 6] 7
3
6
1 3 -1 -3 5 [3 6 7]
3
7
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

思路
采用递增队列和递减队列。
最小值:在一个窗口中,如果来了一个小的数,那么比此数大的数就永远不会成为最小值了,所以我们在入队的时候,需要把此数前面比他大的数全部出队,联想一下,每次入队后,每个元素的左边都会比他小,
这里只需要存元素的下标即可。
具体入队过程如下:(“【】”表示队列)

①10入队时 【】 队空 10入队
②3入队时 【10】 队尾元素10 10>3 10出队;队空,3入队
③7入队时 【3】 队尾元素3 3<7 7入栈
④4入队时 【3 7】 队尾元素7 7>4 7出队;3<4,4入队
⑤12入队时 【3 4】 队尾元素4 12>4 12入队
⑥最后的队 【3 4 12】

代码

#include <iostream>
#include <deque>
#include <vector>
#include<cstdio>
using namespace std;
const int maxn=1e6+10;
int num[maxn];
deque<int> qmax,qmin;
vector<int> num_max,num_min;
int n,k;
int main() 
{
	scanf("%d%d",&n,&k);
    for (int i=0;i<n;i++) scanf("%d",&num[i]);
    
    //求最大值 队列保持单点递减,控制范围,
    for (int i=0;i<n;i++) 
	{
        while ( !qmax.empty() && num[i]>num[qmax.back()] )
		{
			qmax.pop_back();//保持单调递减
		 } 
			
        qmax.push_back(i);//存该值下标
        // 将队列头部值不在窗口内的清除
        while ( i-k>=qmax.front() ) 
			qmax.pop_front();
        if( i>=k-1 ) 
		{
            num_max.push_back( num[qmax.front()] );
        }
    }
    
    //同理,求窗口最小值,队列保持单调递增,
    for (int i=0;i<n;i++) 
	{
        while ( !qmin.empty() && num[i]<num[qmin.back()] ) 
			{
				qmin.pop_back();
			}
        qmin.push_back(i);
        while ( i-k>=qmin.front() ) 
			qmin.pop_front();
        if( i>=k-1 ) 
		{
			num_min.push_back( num[qmin.front()] );	
		}
    }
    
    for (int i=0;i<num_min.size(); i++) 
		printf("%d ",num_min[i]);
    cout<<endl;
    for (int i=0;i<num_max.size(); i++) 
		printf("%d ",num_max[i]);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值