第三周 线性数据结构

这周说实话学习量非常的大,也可能是我太菜了 /(ㄒoㄒ)/~~

做测评我只做最真的

第一题

洛谷

P3613 【深基15.例2】寄包柜

题目描述

超市里有 n(1<= n<=10^5)个寄包柜。每个寄包柜格子数量不一,第 i 个寄包柜有 ai​(1≤ai​≤10^5) 个格子,不过我们并不知道各个 ai​ 的值。对于每个寄包柜,格子编号从 1 开始,一直到 ai​。现在有 q(1<= q<=10^5)次操作:

  • 1 i j k:在第 ii 个柜子的第 jj 个格子存入物品 k(0≤k≤10^9)。当 k=0k=0 时说明清空该格子。
  • 2 i j:查询第 ii 个柜子的第 jj 个格子中的物品是什么,保证查询的柜子有存过东西。

已知超市里共计不会超过 10^7个寄包格子,ai​ 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然也有可能某些寄包柜中一个格子都没有。

输入格式

第一行 2 个整数 n 和 q,寄包柜个数和询问次数。

接下来 q 个整数,表示一次操作。

输出格式

对于查询操作时,输出答案,以换行隔开。

输入输出样例

输入 #1

5 4
1 3 10000 118014
1 1 1 1
2 3 10000
2 1 1

输出 #1 

118014
1

 面对这题,审完题之后发现,题目的意思就是

有n个柜子,柜子里面还有格子

一开始我们就可能会想到二维数组a[n][m]  n个柜子,柜子里面m个格子里面储存不同的值

但是这样的话我们就得定义一个10^5 * 10^5 的二维数组,太大了

这时候我们想到map,stl里面的一个容器

map可以储存一个键值对,那我们一个柜子格子(键)对应一个物品(值)

对于两个数据构成的一个数据,我们可以用pair来储存

所以我们采用map<pair<>,int> 来对这题进行解决

pair

pair可以把两个数据合成为一个数据,pair<数据类型1,数据类型2> p(v1,v2)创建一个pair里面对应的元素分别为v1,v2;可以用pair.first来访问v1,用pair.second来访问v2;

也可以用pair<v1的数据类型,v2的数据类型> p1(p)把p里面的数据拷贝给p1;

map

map使用map<数据类型1,数据类型2> 变量名来定义

map<char,int> mp 通过mp['c']=20;先定义一个mp的map数据,再把mp里面的char赋值为c,int赋值为20;通过键来获取这个键所对应的值,或者通过mp->first来获取键变量,通过mp->second获取值变量

代码实现如下

#include <bits/stdc++.h>
#include <map>
using namespace std;
int main(){
	int n,q,a,c,d,e;
	cin>>n>>q;
	map<pair<int,int>,int> b; 
	for(int i=1;i<=q;i++){
		cin>>a>>c>>d;
		if(a==1){
			cin>>e;
			b[{c,d}]=e;
		}
		else{
			cout<<b[{c,d}]<<endl;
		}
	}
}

 

第二题,有点折磨人的一题

洛谷P1241 括号序列

题目描述

定义如下规则:

  1. 空串是「平衡括号序列」
  2. 若字符串 S 是「平衡括号序列」,那么 [S] 和 (S) 也都是「平衡括号序列」
  3. 若字符串 A 和 B 都是「平衡括号序列」,那么 AB(两字符串拼接起来)也是「平衡括号序列」。

例如,下面的字符串都是平衡括号序列:

()[](())([])()[]()[()]

而以下几个则不是:

([])(())([()

现在,给定一个仅由 ()[]构成的字符串 s,请你按照如下的方式给字符串中每个字符配对:

  1. 从左到右扫描整个字符串。
  2. 对于当前的字符,如果它是一个右括号,考察它与它左侧离它最近未匹配的的括号。如果该括号与之对应(即小括号匹配小括号,中括号匹配中括号),则将二者配对。如果左侧未匹配的左括号不存在或与之不对应,则其配对失败。

配对结束后,对于 s 中全部未配对的括号,请你在其旁边添加一个字符,使得该括号和新加的括号匹配。

输入格式

输入只有一行一个字符串,表示 s。

输出格式

输出一行一个字符串表示你的答案。

输入输出样例

输入 #1

 

([() 

输出 #1 

 

()[]()

 输入 #2

([)

输出 #2  

()[]()

说明/提示

数据规模与约定

对于全部的测试点,保证 ss 的长度不超过 100,且只含 ()[] 四个字符。

 通过题意,我们可以知道,我们就是要去找一个右括号)]它能找到的最近的左括号,如果这个左括号没有匹配成功且能和这个右括号匹配,那这两个括号我们就标记为匹配成功

例如([)] 对于第一个出现的右括号)来说,它左边第一个左括号是[找到了它,但是二者不可以匹配,那么这一对匹配不成功,于是我们就帮他补齐([()]对于下一个右括号]找到它左边第一个左括号(它现在是匹配成功的那么下一个[这个是未匹配的,能和]匹配,所以他们两走在了一起判断完了检查一下这个字符串第一个(还是单身的,那我们帮他找一个伴侣,所以匹配完是()[()]

那我们该怎么办,一个个的去检查然后标记这个括号是否匹配成功,然后遍历找第一个未匹配成功的左括号,然后再遍历字符串找出没有匹配成功的左括号给他补齐

#include <bits/stdc++.h>
using namespace std;
char p[101];
int jl[101],top;
int main()
{
	string s;
	cin>>s;
	int a=s.length();
	for(int i=0;i<a;i++){
		if(s[i]=='(') jl[++top]=i,p[i]=')';//左括号我们记录下这个括号的位置 再帮他配对 
		else if(s[i]=='[') jl[++top]=i,p[i]=']';
		else if(s[i]==')'||s[i]==']'){//右括号我们进行匹配 找到第一个左括号 
			if(!top||p[jl[top]]!=s[i]){//右括号是第一个或者没有找到匹配的 
			   if(s[i]==')') p[i]='(';
			   else p[i]='[';//用p来补全不能匹配的括号 
		    }
		    else p[jl[top--]]=' ';//能匹配到左括号,把这个左括号对应的右括号删去 
		}
	}
	for(int i=0;i<a;i++){
		if(p[i]=='['||p[i]=='(') cout<<p[i];
		cout<<s[i];
		if(p[i]==']'||p[i]==')') cout<<p[i];
	} 
}

这边我们先帮他一个个配对好了,对应在字符数组p里面,如果有配对成功的,那我们就把字符数组p里面对应的括号删除,就形成了一个p与输入字符串s互补的一个完整答案

这边我们得注意一个特殊的例子,就是)和]在字符串首位置的时候中特殊条件

输出先输出p里面的[和(,因为字符串第一个字符可能就是)或],然后输出字符串,在输出p里面的]和),这样就可以 省略掉遍历字符串再去寻找未匹配的括号的操作了

洛谷P1449 后缀表达式

题目描述

所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。

如:\texttt{3*(5-2)+7}3*(5-2)+7 对应的后缀表达式为:\texttt{3.5.2.-*7.+@}3.5.2.-*7.+@。在该式中,@ 为表达式的结束符号。. 为操作数的结束符号。

输入格式

输入一行一个字符串 ss,表示后缀表达式。

输出格式

输出一个整数,表示表达式的值。

输入输出样例

输入 #1

3.5.2.-*7.+@

 输出 #1

16 

说明/提示

数据保证,1≤∣s∣≤50,答案和计算过程中的每一个值的绝对值不超过 10^9。

怎么说,就是一个.对应一个运算符和@,一个字符串,遇到@就结束,那我们完全可以改成一个字符数组一直输入直到输入的数据为@时停止;

然后运算一直是两个数进行运算,那我们就可以定义一个数组来储存数字然后再慢慢地往回加进行i--数组往前读取;

例如上面,就是第一个输入的运算符-对它前面的两个

 5,2得到数字3然后再找到下一个运算符*,3*3得到9,在下一个运算符+得到9+7得到最终值16

那我们就可以开始写代码了

#include <bits/stdc++.h>
using namespace std;
long long sum[1005];
int main()
{
    char s;
    long long i=0,a=0;
    while((s=getchar())!='@'){
        if(s>='0' && s<='9')
        {
        	a*=10;
        	a+=s-'0';
        }
        else if(s=='.'){
        	i++;
        	sum[i]=a;
        	a=0;
		}
		else if(s=='-'){
			sum[i-1]=sum[i-1]-sum[i];
			sum[i]=0;
			i--;
		}
		else if(s=='+'){
			sum[i-1]=sum[i-1]+sum[i];
			sum[i]=0;
			i--;
		}
		else if(s=='*'){
			sum[i-1]=sum[i-1]*sum[i];
			sum[i]=0;
			i--;
		}
		else if(s=='/'){
			sum[i-1]=sum[i-1]/sum[i];
			sum[i]=0;
			i--;
		}
	}
	cout<<sum[1];
    return 0;
} 

题目描述

一个学校里老师要将班上 NN 个同学排成一列,同学被编号为 1\sim N1∼N,他采取如下的方法:

  1. 先将 11 号同学安排进队列,这时队列中只有他一个人;

  2. 2-N2−N 号同学依次入列,编号为 ii 的同学入列方式为:老师指定编号为 ii 的同学站在编号为 1\sim(i-1)1∼(i−1) 中某位同学(即之前已经入列的同学)的左边或右边;

  3. 从队列中去掉 M(M<N)M(M<N) 个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

输入格式

第 11 行为一个正整数 NN,表示了有 NN 个同学。

第 2\sim N2∼N行,第 ii 行包含两个整数 k,pk,p,其中 kk 为小于 ii 的正整数,pp 为 00 或者 11。若 pp 为00,则表示将 ii 号同学插入到 kk 号同学的左边,pp 为 11 则表示插入到右边。

第 N+1N+1 行为一个正整数 MM,表示去掉的同学数目。

接下来 MM 行,每行一个正整数 xx,表示将 xx 号同学从队列中移去,如果 xx 号同学已经不在队列中则忽略这一条指令。

输出格式

11 行,包含最多 NN 个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。

输入输出样例

 输入 #1

4
1 0
2 1
1 0
2
3
3

 输出 #1

2 4 1

 

说明/提示

样例解释:

将同学 2 插入至同学 1 左边,此时队列为:

2 1

将同学 3 插入至同学 2 右边,此时队列为:

2 3 1

将同学 4 插入至同学 1 左边,此时队列为:

2 3 4 1

将同学 3 从队列中移出,此时队列为:

2 4 1

同学 3 已经不在队列中,忽略最后一条指令

最终队列:

2 4 1

数据范围

对于 20% 的数据,有 1≤N≤10;

对于 40% 的数据,有 1≤N≤1000;

对于 100% 的数据,有 1≤N,M≤100000。

解释的很清楚了,那我们得对学生进行分析,因为是要插入和删除的,那我们首先想到链表,

链表就能进行插入和删除,链表结构体数组的索引表示学生的学号,在链表内定义一个resist来确定这个学生是否插入到链表内或者是否被删除,输出时我们就按照链表的顺序进行输出,一个接一个地输出,这边使用的是双向链表来实现这个功能

for(int i=a[0].r;i;i=a[i].r){
        if(a[i].resist==0) cout<<i<<' ';
    }

用这段代码来实现链表的对应的索引一个一个地准确输出

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m;
struct T{
	int l,r;
	int resist;
}a[maxn]={0};
void add(int i,int p,int f){//i是新增的同学 **顺序不能乱,乱了就会出错 
	if(f==1){//i插入在p右边 
	    a[i].l=p;
		a[i].r=a[p].r;
		a[a[i].r].l=i;
		a[p].r=i; 
	}//实现在p和p右边的位置之间插入一个i 
	else{//i插入在p的左边 
        a[i].r=p;
		a[i].l=a[p].l;
		a[a[i].l].r=i;
		a[p].l=i;
	}
}
int main(){
	int p,f,b;
	cin>>n;
	a[0].l=0,a[0].r=0;//重置第一位同学,假设第一位同学插入在第0的同学的旁边 
	add(1,0,1);
	for(int i=2;i<=n;i++){//0表示左边,1表示右边用f来记录,i就是我要插入的对象,p是我要插入的地方 
		cin>>p>>f;
		add(i,p,f);
	}
	cin>>m;
	while(m--){
		cin>>b;
		a[b].resist=1;
	}
	for(int i=a[0].r;i;i=a[i].r){
		if(a[i].resist==0) cout<<i<<' ';
	}
	return 0;
}

上周博客写完了,又得去赶这周的作业了,生活不就是苦中作乐吗?

下周见呜呜呜

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值