程设第五周

第一题

简单题意

给一个直方图,求直方图中的最大矩形的面积。多组数据,对于一组数据,只有一行,第一个为n,表示有n个整数,接下来是n个整数hi,n<=100 000,hi<=1000 000 000。n为0时表示结束。

思路

我们开一个栈,设其为单调递增,然后处理数据。

如果进入的数大于等于栈顶元素,入栈,并且记录入栈的位置,这样我们就知道以当前数字为矩形最左的边的位置,一个要注意的点就是入下列数据:2、1,当1进入栈时,2被弹出,此时栈中没有元素,那么我们就要将1的入栈位置记为1而不是2,因为如果以1为高度,第一个元素也可以取到1。

如果进入的数小于栈顶元素,栈顶元素弹出,并且计算弹出元素值*(i-入栈位置),结果若大于ans,存入ans。

此外还有一个地方就是不能直接使用int计算,因为hi<=1000 000 000,n<=100 000,所以要处理一下。

代码
#include<iostream>

using namespace std;

int main()
{
	int n,x,l,tot,left,right,ans1,ans2,sum1,sum2;
	int a[100010],b[100010]; 
	cin>>n;
	while (n!=0)
	{
		tot=1;
		cin>>a[1];
		b[1]=1;
		ans1=0;
		ans2=0;
		for (int i=2;i<=n;i++)
		{
			cin>>x;
			if (x>=a[tot])
			{
				tot++;
				a[tot]=x;
				if (tot!=1)	b[tot]=i; 
				else b[tot]=1;
			}
			else
			{
				while (x<a[tot] && tot>0)
				{
					l=i-b[tot];
					left=a[tot]/100000;
					right=a[tot]%100000;
					sum1=l*left;
					sum2=l*right;
					if (ans1<sum1 || (ans1==sum1 && ans2<sum2))
					{
						ans1=sum1;
						ans2=sum2;
					}
					tot--;
				}
				tot++;
				a[tot]=x;
			}
		}
		while (tot>0)
		{
			l=n-b[tot]+1;
			left=a[tot]/100000;
			right=a[tot]%100000;
			sum1=l*left;
			sum2=l*right;
			if (ans1<sum1 || (ans1==sum1 && ans2<sum2))
			{
				ans1=sum1;
				ans2=sum2;
			}
			tot--; 
		}
		ans1=ans1+ans2/100000;
		ans2=ans2%100000;
		if (ans1>0)
		{
			cout<<ans1;
			if (ans2<10000) cout<<0;
			if (ans2<1000) cout<<0;
			if (ans2<100) cout<<0;
			if (ans2<10) cout<<0;
			cout<<ans2<<endl;
		}
		else
			cout<<ans2<<endl;
		cin>>n;
	}
	
	return 0;
} 

第二题

简单题意

给出一个长度为n的数组,执行m次操作,操作输入格式为l,r,c,操作具体是将区间[l,r]内的数全部+c。
输入格式为第一行n,m,第二行为n个数,表示数组a,接下来m行,表示m个操作,每个操作为l,r,c。a[i]<=1 000 000,-100 000<=c<=100 000
输出为执行完m个操作之后的数组,每个数用空格隔开。

思路

差分,对于每个操作l,r,c,我们可以将其看为两个操作,l,n,c和r+1,n,-c,这样我们直接开一个数组b,b[i]表示a[i,n]要加b[i]。这样最终答案ans[i]就可以看做a[i]+b[1]+…+b[i],这样我们还可以优化一下,即ans[i]=ans[i-1]+b[i],若这样处理b数组初始就不能为0,而应该是b[i]=a[i]-a[i-1]。

数据要使用long long int存储计算。

代码
#include<iostream>
#include<cstdio>
using namespace std;

long long int a[300000];
long long int b[300000];

int main()
{
	int m,n,l,r,c;
	cin>>n>>m;
	a[0] = 0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld", &a[i]);
		b[i]=a[i]-a[i-1];
	}
	for (int i=0;i<m;i++)
	{
		scanf("%d%d%d",&l,&r,&c);
		b[l]=b[l]+c;
		b[r+1]=b[r+1]-c;
	}
	a[0]=0;
	for (int i=1;i<=n;i++)
	{
		a[i]=b[i]+a[i-1];
		printf("%lld ",a[i]);
	}
	cout<<endl;
	
	return 0;		
}

第三题

简单题意

平衡字符串,给出一个字符串,字符串中只含有QWER这四个字符,要求修改连续的一段,是的QWER四个字符出现的次数相同。必然有解。
输入为一个字符串,输出为最小修改的长度。

思路

要让四个字符的出现次数相同,我们要将出现多的修改为出现少的。如平均5次,Q出现6次,W出现7次,E出现4次,R出现3次,那么我们要将1个Q和2个W改为1个E和2个R,所以在要修改的连续的子串中必须有1个Q和2个W。

我们可以维护l,r,求出[l,r]这个区间中各个字符出现的次数。若该子串满足替换要求,我们将r-l与ans比较,然后l++,看新的子串是否仍然合法,如合法继续更新答案并且l++,直至子串不合法。若子串不合法则r++,直至子串合法。

代码
#include<iostream>
#include<cstring>

using namespace std;

int main()
{
	string s;
	cin>>s;
	int a,b,c,d,l,r,x,y,z,w,ans;
	a=0;	b=0;	c=0;	d=0;
	for (int i=0;i<s.length();i++)
	{
		if (s[i]=='Q')	a++;
		if (s[i]=='W')	b++;
		if (s[i]=='E')	c++;
		if (s[i]=='R')	d++;
	}
	if (a==b && b==c && c==d)
	{
		cout<<0<<endl;
		return 0;
	}
	x=a-s.length()/4;
	y=b-s.length()/4;
	z=c-s.length()/4;
	w=d-s.length()/4;
	a=0;b=0;c=0;d=0;
	l=0; r=0;
	ans=s.length();
	bool o=true;
	while (r<s.length())
	{
		if (s[r]=='Q')	a++;
		if (s[r]=='W')	b++;
		if (s[r]=='E')	c++;
		if (s[r]=='R')	d++;
		r++;
		while (a>=x && b>=y && c>=z && d>=w && l<=r)
		{
			if (r-l<ans) ans=r-l;
			if (s[l]=='Q')	a--;
			if (s[l]=='W')	b--;
			if (s[l]=='E')	c--;
			if (s[l]=='R')	d--;
			l++;
//			cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
//			cout<<l<<" "<<r<<endl;
		}
	}
	while (a>=x && b>=y && c>=z && d>=w && l<=r)
	{
		if (r-l<ans) ans=r-l;
		if (s[l]=='Q')	a--;
		if (s[l]=='W')	b--;
		if (s[l]=='E')	c--;
		if (s[l]=='R')	d--;
		l++;
//		cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
//		cout<<l<<" "<<r<<endl;
	}
	cout<<ans<<endl;
	
	return 0;
}

第四题

简单题意

滑动窗口,一个长度为n的数列和大小为k的窗口,窗口可以在数列上移动,要求给出窗口从左到右移动,每次窗口内的最大值和最小值。

输入格式为第一行n,k,第二行为长度为n的数列。

输出格式为两行,第一行为最小值,第二行为最大值。

思路

使用单调队列,我们使用i表示窗口的右端,即当前窗口位置为[i-k+1,i],i可以使用for循环。最小值我们维护一个单调递增队列,队首元素就是最小值,若当前加入元素比队尾元素小,根据单调队列的定义,那么就会出现一个山峰,如:1,4,3,但是这个4是不会成为最小值的,因为每个元素都要往前走,最后破坏队列,所以要删掉。

然后因为窗口的滑动,有些元素会过时被删掉,我们的i是使用for循环来移动的,所以元素过时是从队首开始过时,因为新加入的元素是加到队尾的。我们判断一下队首元素的编号q[head]与i-k的关系,如果q[head]<=i-k,说明元素已经过时,要弹出,head++。

最后当i>=w-1时,即窗口完全在数列中,我们就可以输出a[q[head]],这就是当前窗口的最小值。

最大值与最小值同理。

代码
#include<iostream>
#include<cstdio>

using namespace std;

int a[1000100],q[1000100]; 

int main()
{
 
    int n,w,head,tail,ans,count;
    cin>>n>>w;
    for (int i=0;i<n;i++)
    	scanf("%d",&a[i]);
    q[0]=0;
    head=1;	tail=0;
    for(int i=0;i<n;i++)
	{
		if (head<=tail)
		{
			count=q[tail];
			ans=a[count];
			while (a[i]<ans && head<=tail)
			{
				tail--;
				if (head<=tail)
				{
					count=q[tail];
					ans=a[count];
				}		
			}
		}
		tail++;
		q[tail]=i;
		while (q[head]<=i-w)
			head++;
		if(i>=w-1)
			printf("%d ",a[q[head]]);
	}
	cout<<endl;
	
    head=1;	tail=0;
    for(int i=0;i<n;i++)
	{
		if (head<=tail)
		{
			count=q[tail];
			ans=a[count];
			while (a[i]>ans && head<=tail)
			{
				tail--;
				if (head<=tail)
				{
					count=q[tail];
					ans=a[count];
				}		
			}
		}
		tail++;
		q[tail]=i;
		while (q[head]<=i-w)
			head++;
		if(i>=w-1)
			printf("%d ",a[q[head]]);
	}
	cout<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值