2021_GDUT_Data Structure

描述:栈是一种“后入先出”的数据结构。

支持操作:入栈(push),出栈(pop);

单调栈:借助单调性处理问题,即使排除不可能的选项;

单调栈的应用: 对于可以转化某一元素为找到其右边第一个大于(小于等于)的元素的题目,可以利用单调栈的单调性优化求解.
例题讲解:

在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$“标注,不能匹配的右括号用”?"标注.

分析:忽略大小写字母的输入,从左往右一个个读取字符,当字符为左括号时进栈,右括号时将栈顶元素出栈,若栈顶为空则不能匹配,运用栈这种数据结构即可解决此题;

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
char s[105],ans[105];
int t[105];
int main()
{
	while(~scanf("%s",s+1))
	{
		int len = strlen(s+1),tot = 0;
		for(int i = 1;i <= len;++i)
		{
			if(s[i]=='(')
				t[++tot] = i;
			else if(s[i]==')'&&tot)
				ans[i] = ans[t[tot--]] = ' ';
			else if(s[i]==')') ans[i] = '?';
			else ans[i] = ' ';
		}
		printf("%s\n",s+1);
		for(int i = 1;i <= tot;++i) ans[t[i]] = '$';
		for(int i = 1;i <= len;++i) printf("%c",ans[i]);
		cout << endl;
	}
	return 0;
 } 

例题:
在这里插入图片描述
分析: 单调栈基本题目;

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll h[100005],tot,w[100005];
int main()
{
	int n;cin >> n;
	while(n)
	{
		tot = 0;h[tot] = -1;
		ll ans = -1;
		for(int i = 0;i <= n;++i) w[i] = 1;
		for(int i = 1;i <= n;++i)
		{
			ll t,cnt = 0;scanf("%lld",&t);
			while(t<=h[tot])
			{
				cnt += w[tot];
				ans = max(ans,h[tot--]*cnt);
			}
			h[++tot] = t;
			w[tot] = cnt+1;
		}
		ll cnt = 0;
		while(tot)
		{
			cnt += w[tot];
			ans = max(ans,h[tot--]*cnt);
		}
		cout << ans << endl;
		scanf("%d",&n);
	}
	return 0;
}

队列:

描述:一种先进先出的数据结构;

例题:

蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
本题中,我们将用符号 ⌊c⌋ 表示对 c 向下取整,例如:⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。
蛐蛐国里现在共有 n 只蚯蚓(n 为正整数)。每只蚯蚓拥有长度,我们设第 i 只蚯蚓的长度为 ai(i=1,2,…,n),并保证所有的长度都是非负整数(即:可能存在长度为 0 的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数 p(是满足 0<p<1 的有理数)决定,设这只蚯蚓长度为 x,神刀手会将其切成两只长度分别为 ⌊px⌋ 和 x−⌊px⌋ 的蚯蚓。特殊地,如果这两个数的其中一个等于 0,则这个长度为 0 的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加 q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要 m 秒才能到来 ……(m 为非负整数)
蛐蛐国王希望知道这 m 秒内的战况。具体来说,他希望知道:
m 秒内,每一秒被切断的蚯蚓被切断前的长度(有 m 个数);
m 秒后,所有蚯蚓的长度(有 n+m 个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你 ……

分析:对于整体加的操作我们可以替换为单体减的操作,用del表示数据的值与真实值差据,通过建立三个队列即可解题;

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
queue<ll>q1,q2,q3;
ll a[100005];

int main()
{
    int n,m,q,u,v,t;scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
    for(int i = 1;i <= n;i++)scanf("%lld",&a[i]);
    sort(a + 1,a + 1 + n);
    for(int i = n;i >= 1;i--)q1.push(a[i]);
    
    ll delta = 0;
    for(int i = 1;i <= m;i++)
    {
        ll mx = -1e9;
        int w = 0;
        if(q1.size() && mx < q1.front())
        {
            w = 1;
            mx = q1.front();
        }
        if(q2.size() && mx < q2.front())
        {
            w = 2;
            mx = q2.front();
        }
        if(q3.size() && mx < q3.front())
        {
            w = 3;
            mx = q3.front();
        }
        if(w == 1)q1.pop();
        if(w == 2)q2.pop();
        if(w == 3)q3.pop();
        mx = mx + delta;
        
        if(i % t == 0)
        {
            printf("%lld ",mx);
        }
        ll x1 = mx * u / v - delta - q,x2 = mx - mx * u / v - delta - q;
        q2.push(x1);q3.push(x2);
        delta += q;
    }
    printf("\n");
    
    for(int i = 1;i <= m + n;i++)
    {
        ll mx = -1e9;
        int w = 0;
        if(q1.size() && mx < q1.front())
        {
            w = 1;
            mx = q1.front();
        }
        if(q2.size() && mx < q2.front())
        {
            w = 2;
            mx = q2.front();
        }
        if(q3.size() && mx < q3.front())
        {
            w = 3;
            mx = q3.front();
        }
        if(w == 1)q1.pop();
        if(w == 2)q2.pop();
        if(w == 3)q3.pop();
        
        if(i % t == 0)
        {
            printf("%lld ",mx + delta);
        }
    }
    return 0;
}


方块:通过适当的划分,预处理一部分信息并保存下来,用空间换取时间,达到时空平衡;

使用:通常将一个数组划分为sqrt(N)个;

例题:

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。

分析:分块解决,用L代表块的左端,R代表块的右端,对块内部进行排序可以快速得到答案;

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
const ll siz = 1e5;
ll a[siz],add[siz],b[siz];
int L[siz],R[siz],pos[siz],n;
void div()
{
	int q = sqrt(n);
	for(int i = 1;i <= q;++i)
	{
		L[i] = (i-1)*q+1;
		R[i] = i*q;
		add[i] = 0;
		sort(b+L[i],b+R[i]+1);
		for(int j = L[i];j <= R[i];++j) pos[j] = i;
	}
	if(R[q]!=n)
	{
		L[q+1] = R[q]+1;
		R[q+1] = n;
		add[q+1] = 0;
		sort(b+L[q+1],b+n+1);
		for(int i = R[q]+1;i <= n;++i) pos[i] = q+1;
	}
}
void ad(int l,int r,ll d)
{
	int p = pos[l],q = pos[r];
	if(p^q)
	{
		for(int i = l;i <= R[p];++i) a[i] += d;
		for(int i = L[p];i <= R[p];++i) b[i] = a[i];
		sort(b+L[p],b+R[p]+1);
		for(int i = p+1;i < q;++i) add[i] += d;
		for(int i = L[q];i <= r;++i) a[i] += d;
		for(int i = L[q];i <= R[q];++i) b[i] = a[i];
		sort(b+L[q],b+R[q]+1);
	}
	else
	{
		for(int i = l;i <= r;++i) a[i] += d;
		for(int i = L[p];i <= R[p];++i) b[i] = a[i];
		sort(b+L[p],b+R[p]+1);
	}
	//for(int i = 1;i <= n;++i) printf("%lld ",b[i]+add[pos[i]]);cout << endl;
}
ll query(int l,int r,ll c)
{
	int p = pos[l],q = pos[r];ll ans=0;
	if(p^q)
	{
		for(int i = l;i <= R[p];++i) if(a[i]+add[p]<c) ++ans; 
		for(int i = p+1;i < q;++i) ans += lower_bound(b+L[i],b+R[i]+1,c-add[i])-b-L[i];
		for(int i = L[q];i <= r;++i) if(a[i]+add[q]<c) ++ans;
	}
	else for(int i = l;i <= r;++i) if(a[i]+add[p]<c) ++ans;
	return ans;
}
int main()
{
	cin >> n;
	for(int i = 1;i <= n;++i)
	{
		scanf("%lld",a+i);
		b[i] = a[i];
	}
	div();
	for(int t = 1;t <= n;++t)
	{
		int op,l,r;ll c;scanf("%d%d%d%lld",&op,&l,&r,&c);
		if(op)
		{
			ll ans = query(l,r,c*c);
			printf("%lld\n",ans);
		}
		else
			ad(l,r,c);
	}
	return 0;
}

总结:不同数据结构的灵活使用可以大幅减少算法的时间复杂度,解题时应注意算法可否利用不同数据结构进行结构减少时间复杂度;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值