week5——作业(线性结构的练习)

最大矩形 :

问题描述

题目简述

给一个直方图,求直方图中的最大矩形的面积。

输入/输出格式

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

样例

输入样例:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
输出样例:
8
4000

问题分析

解题思路

总的来说,这个题可以使用单调栈解决。即,我们需要通过单调栈来求解某一个高度x可以向左右延伸的最大长度。这样的话,我们使用单增栈,从前向后遍历整个数组,当栈顶元素大于位于i位置的元素值时,我们将这个元素弹出,并记下该元素的位置。这样,即可求出某个高度向右延伸的最大边界。同理,我们从后向前遍历整个数组时,即可求出左边界。两者相减后乘高度即可求出面积,取最大的结果即为所求。

参考代码
#include <iostream>
#include <stack>

using namespace std;

long long r[100100],l[100100];
stack<long long> s;
long long n;
long long height[100100];

void solve1()
{
	for(long long i=1;i<=n;i++)
	{
		while(!s.empty()&&height[s.top()]>height[i])
		{
			r[s.top()]=i-1;
			s.pop();
		}
		s.push(i);
	}
	while(!s.empty())
	{
		r[s.top()]=n;
		s.pop();
	}
}

void solve2()
{
	for(long long i=n;i>=1;i--)
	{
		while(!s.empty()&&height[s.top()]>height[i])
		{
			l[s.top()]=i+1;
			s.pop();
		}
		s.push(i);
	}
	while(!s.empty())
	{
		l[s.top()]=1;
		s.pop();
	}
}

int main()
{

	scanf("%lld",&n);
while(n!=0)
{ 
	for(long long i=1;i<=n;i++)
	{
		scanf("%d",&height[i]);
	}
	solve1();
	solve2();
	long long ans=0;
	for(long long i=1;i<=n;i++)
	{
		//printf("the message:%lld,%lld,%lld\n",height[i],r[i],l[i]);
		long long t=height[i]*(r[i]-l[i]+1);
		if(t>ans) ans=t;
	}
	printf("%lld\n",ans);
	scanf("%lld",&n);
}
	return 0;
}

心得体会

很常规的一个单调栈的小练习。题目不难,主要还是练习单调栈的知识。做的时候在单调栈的弹出条件判断时有些不熟练。但做完这个题后,也加深了不少印象。

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 qoperations.

Could you help TT find the answer?

输入/输出格式

输入格式:
The first line contains two integers n,q (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)

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) for the i-th operation.
输出格式:
Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the i-th city.

样例

输入样例:
4 2
-3 6 8 4
4 4 -2
3 3 1
输出样例:
-3 6 9 2

问题分析

解题思路

本题数据规模过大,导致暴力求解不可取(O(n^2)的复杂度太高了,会超时),因此,考虑降低复杂度。这里使用了差分的方法,即构造一个新数列,该数列中,除第一项等于原数列的第一项外,其他项均为数列的第i项与第i-1项之差。这样处理之后,原数列中某范围中的数据统一加减值转化为新数列中两项的运算。而新数列逆推回原数列只需要通过递推公式即可完成。整个复杂度降为了O(n)。

参考代码
#include <iostream>

using namespace std;

int main()
{
	int n,q;
	scanf("%d %d",&n,&q);
	long long* array=new long long[n];
	long long* b=new long long[n];
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&array[i]);
	}
	b[0]=array[0];
	for(int i=1;i<n;i++)
	{
		b[i]=array[i]-array[i-1];
	}
	int x,y,z;
	for(int i=1;i<=q;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		b[x-1]+=z;
		b[y]-=z;
	}
	array[0]=b[0];
	for(int i=1;i<n;i++)
	{
		array[i]=array[i-1]+b[i];
	}
	for(int i=0;i<n;i++)
	{
		printf("%lld ",array[i]);
	}
	return 0;
}

心得体会

write some words here.

平衡字符串 :

问题描述

题目简述
一个长度为 n 的字符串 s,其中仅包含 'Q', 'W', 'E', 'R' 四种字符。

如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。

现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?

如果 s 已经平衡则输出0。
输入/输出格式

输入格式:
一行字符表示给定的字符串s
输出格式:
一个整数表示答案

样例

输入样例:
QQWE
输出样例:
1

问题分析

解题思路

本题数据规模过大,导致暴力求解不可取(O(n^2)的复杂度太高了,会超时),因此,考虑降低复杂度。这里使用尺取法。首先在输入字符串的时候统计每个字母出现的个数。之后,根据输入字符串的长度,求出平衡字符串的每个字母的标准个数,并于统计数量相减。之后使用尺取法,从左到右用双指针遍历字符串。求出子串的性质,并比较,最后取答案的最小值即可。

参考代码
#include <iostream>
#include <map>
#include <vector>

using namespace std;

char str[100010];
int n;
map<char,int> mp;
vector<char> v={'Q','W','E','R'};
int main()
{
	mp['Q']=0;
    mp['W']=0;
    mp['E']=0;
    mp['R']=0;
    scanf("%s",str);
    int i=0;
    int n=0;
    while(str[i]!=0)
    {
    	n++;
    	mp[str[i]]++;
    	i++;
	}
	bool balance=true;
	int std=n/4;
	for(int i=0;i<v.size();i++)
	{
		mp[v[i]]-=std;
		if(mp[v[i]]>0) balance=false;
	}
	if(balance)
	{
		printf("%d\n",0);
		return 0;
	}
	else 
	{
		int left=1,right=1,ans=n;
		while(left<=right&&right<=n)
        {
        	bool b=true;
        	mp[str[right]]--;
        	while(b)
        	{
        		for(int i=0;i<v.size();i++)
        		{
        			if(mp[v[i]]>0)
        			{
        				b=false;
        				break;
					}
				}
				if(b)
				{
					ans=min(ans,right-left+1);
					mp[str[left]]++;
					left++;
				}
			}
			right++;
		}
		printf("%d\n",ans);
		return 0;	
	}
}

心得体会

比较抽象的一道尺取法问题。做题的时候比较困扰的一点是如何对应到尺取上去。感觉一点是比较难想的。

滑动窗口

问题描述

题目简述

ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.

输入/输出格式

输入格式:
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
输出格式:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。

样例

输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7

问题分析

解题思路

本题是一个单调队列问题。即,在求最大值时使用一个单减队列。其最大长度为k,且只能同时存储数列中下标相差最大为k的元素。当队尾的元素值小于入队元素或者队头的元素下表比新入队的元素小k时,删除对应元素。这样,可以保证队首的元素永远是部分最大的。即可解决问题。相应的,求最小值的话只需要用一个单增队列即可完成。

参考代码
#include <iostream>
#include <deque>

using namespace std;

deque<int> dq;

int min_num[1000010];
int max_num[1000010];
int n,k;
int num[1000010];

void solve1()
{
	dq.clear(); 
	for(int i=1;i<=n;i++)
	{
		while(!dq.empty()&&num[dq.back()]>num[i])
		{
			dq.pop_back();
		}
		while(!dq.empty()&&i-dq.front()>=k)
		{
			dq.pop_front();
		}
		dq.push_back(i);
		min_num[i]=num[dq.front()];
	}
}

void solve2()
{
	dq.clear();
	for(int i=1;i<=n;i++)
	{
		while(!dq.empty()&&num[dq.back()]<num[i])
		{
			dq.pop_back();
		}
		while(!dq.empty()&&i-dq.front()>=k)
		{
			dq.pop_front();
		}
		dq.push_back(i);
		max_num[i]=num[dq.front()];
	}
}

int main()
{
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
	}
	solve1();
	solve2();
	for(int i=k;i<=n;i++)
	{
		printf("%d ",min_num[i]);
	}
	printf("\n");
	for(int i=k;i<=n;i++)
	{
		printf("%d ",max_num[i]);
	}
	return 0;
}

心得体会

本题也是一道基础的单调队列练习题。题目不难,加上有了单调栈的铺垫,因此做起来还是比较轻松的。单调栈和单调队列是比较类似的结构,因此可以类比记忆和使用。

发布了10 篇原创文章 · 获赞 1 · 访问量 170

猜你喜欢

转载自blog.csdn.net/qq_43715114/article/details/105030651