(一)目录
L2-014 列车调度
L2-024 部落
L2-033 简单计算器
L2-042 老板的作息表
L2-041 插松枝
(二)题目
L2-014 列车调度
火车站的列车调度铁轨的结构如下图所示。
两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N
条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有9趟列车,在入口处按照{8,4,2,5,3,9,1,6,7}的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?
输入格式:
输入第一行给出一个整数N
(2 ≤ N
≤105),下一行给出从1到N
的整数序号的一个重排列。数字间以空格分隔。
输出格式:
在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。
输入样例:
9
8 4 2 5 3 9 1 6 7
输出样例:
4
思路:这道题就是只能插入小的,如果遇到大的,就把小的删除,插入大的。
lower_bound:返回一个 iterator 它指向在[first,last)标记的有序序列中可以插入value,而不会破坏容器顺序的第一个位置,而这个位置标记了一个不小于value 的值。
upper_bound:返回一个 iterator 它指向在[first,last)标记的有序序列中可以插入value,而不会破坏容器顺序的最后一个位置,而这个位置标记了一个不大于value 的值。
以上两种bound,需要在[beging , end)中元素已经排序。其在可随机访问迭代器中,拥有很高的查找效率。
迭代器:set<int>::interator it;
代码:
#include <bits/stdc++.h>
#include<iostream>
#include<set>
using namespace std;
int main()
{
int n;
cin>>n;
set<int>sc;
for(int i=0;i<n;i++)
{
int k;
scanf("%d",&k);
set<int>::iterator it=sc.lower_bound(k);
if(it!=sc.end())
{
sc.erase(it);
sc.insert(k);
}
else
sc.insert(k);
}
cout<<sc.size();
}
L2-024 部落
在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。
输入格式:
输入在第一行给出一个正整数N(≤104),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:
K P[1] P[2] ⋯ P[K]
其中K是小圈子里的人数,P[i](i=1,⋯,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过104。
之后一行给出一个非负整数Q(≤104),是查询次数。随后Q行,每行给出一对被查询的人的编号。
输出格式:
首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y
,否则输出N
。
输入样例:
4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7
输出样例:
10 2
Y
N
思路:这道题是典型的并查集 (找共同祖先)
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 10010;
int par[maxn], rak[maxn], N;
void init(int n){
for(int i = 0; i <= n; i++)
par[i] = i, rak[i] = 0;
}
int findr(int x){
return x==par[x]?x:findr(par[x]);
}
bool isSame(int x, int y){return findr(x)==findr(y);}
void unite(int x, int y){
x = findr(x), y = findr(y);
if(x == y) return;
if(rak[x] < rak[y]){
par[x] = y;
}else{
par[y] = x;
if(rak[x]==rak[y])
rak[x]++;
}
}
int main()
{
init(10000);
int p1, p2, k;
set<int> s1, s2;
cin >> N;
for(int i = 1; i <= N; i++){
cin >> k >> p1;
s1.insert(p1);
for(int j = 2; j <= k; j++){
cin >> p2;
s1.insert(p2);
unite(p1, p2);
}
}
int sum = 0;
for(int i = 1; i <= s1.size(); i++)
if(par[i] == i) sum++; //par[i]==i即表示有一种分类
cout << s1.size() << " " << sum << endl;
cin >> k;
while(k--){
cin >> p1 >> p2;
if(isSame(p1, p2)) cout << "Y\n";
else cout << "N\n";
}
return 0;
}
L2-033 简单计算器
本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示,计算器由两个堆栈组成,一个堆栈 S1 存放数字,另一个堆栈 S2 存放运算符。计算器的最下方有一个等号键,每次按下这个键,计算器就执行以下操作:
- 从 S1 中弹出两个数字,顺序为 n1 和 n2;
- 从 S2 中弹出一个运算符 op;
- 执行计算 n2 op n1;
- 将得到的结果压回 S1。
直到两个堆栈都为空时,计算结束,最后的结果将显示在屏幕上。
输入格式:
输入首先在第一行给出正整数 N(1<N≤103),为 S1 中数字的个数。
第二行给出 N 个绝对值不超过 100 的整数;第三行给出 N−1 个运算符 —— 这里仅考虑 +
、-
、*
、/
这四种运算。一行中的数字和符号都以空格分隔。
输出格式:
将输入的数字和运算符按给定顺序分别压入堆栈 S1 和 S2,将执行计算的最后结果输出。注意所有的计算都只取结果的整数部分。题目保证计算的中间和最后结果的绝对值都不超过 109。
如果执行除法时出现分母为零的非法操作,则在一行中输出:ERROR: X/0
,其中 X
是当时的分子。然后结束程序。
输入样例 1:
5
40 5 8 3 2
/ * - +
输出样例 1:
2
输入样例 2:
5
2 5 8 4 4
* / - +
输出样例 2:
ERROR: 5/0
代码:
#include <bits/stdc++.h>
using namespace std;
int calculate(int a, int b, char c) {
if (c=='+')return a+b;
if (c=='-')return b-a;
if (c=='*')return a*b;
if (c=='/')return b/a;
}
int main(){
int n; cin>>n;
stack<int>digit;
stack<char>op;
// input
for (int i = 0; i < n; ++i) {
int temp; cin>>temp;
digit.push(temp);
}
for (int i = 0; i < n - 1; ++i) {
char temp;cin>>temp;
op.push(temp);
}
// deal
while (digit.size()!=1){
int a = digit.top(); digit.pop();
int b = digit.top(); digit.pop();
char c = op.top(); op.pop();
if (a==0 && c=='/'){
cout<<"ERROR: "<<b<<"/"<<a<<endl;
return 0;
}
digit.push(calculate(a,b,c));
}
cout<<digit.top()<<endl;
}
L2-042 老板的作息表
新浪微博上有人发了某老板的作息时间表,表示其每天 4:30 就起床了。但立刻有眼尖的网友问:这时间表不完整啊,早上九点到下午一点干啥了?
本题就请你编写程序,检查任意一张时间表,找出其中没写出来的时间段。
输入格式:
输入第一行给出一个正整数 N,为作息表上列出的时间段的个数。随后 N 行,每行给出一个时间段,格式为:
hh:mm:ss - hh:mm:ss
其中 hh
、mm
、ss
分别是两位数表示的小时、分钟、秒。第一个时间是开始时间,第二个是结束时间。题目保证所有时间都在一天之内(即从 00:00:00 到 23:59:59);每个区间间隔至少 1 秒;并且任意两个给出的时间区间最多只在一个端点有重合,没有区间重叠的情况。
输出格式:
按照时间顺序列出时间表中没有出现的区间,每个区间占一行,格式与输入相同。题目保证至少存在一个区间需要输出。
输入样例:
8
13:00:00 - 18:00:00
00:00:00 - 01:00:05
08:00:00 - 09:00:00
07:10:59 - 08:00:00
01:00:05 - 04:30:00
06:30:00 - 07:10:58
05:30:00 - 06:30:00
18:00:00 - 19:00:00
输出样例:
04:30:00 - 05:30:00
07:10:58 - 07:10:59
09:00:00 - 13:00:00
19:00:00 - 23:59:59
分析:这道题特别有意思在于数据的输入,如果基本功不牢,步骤真的会很繁琐。
代码:
#include <bits/stdc++.h>
using namespace std;
int n;
vector<pair<string,string>> q;//vector <pair<string,string>>q;
int main(){
cin >> n;
while(n -- ){
string a, b, c;
cin >> a >> b >> c;
q.push_back({a, c});//q.push_back({a,c});
}
q.push_back({"", "00:00:00"});
q.push_back({"23:59:59", ""});
sort(q.begin(), q.end());
int m = q.size();
for (int i = 0; i < m - 1; i ++ )
if (q[i].second != q[i + 1].first)
cout << q[i].second << " - " << q[i + 1].first << endl;
return 0;
}
L2-041 插松枝
人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上,做成大大小小的松枝。他们的工作流程(并不)是这样的:
- 每人手边有一只小盒子,初始状态为空。
- 每人面前有用不完的松枝干和一个推送器,每次推送一片随机型号的松针片。
- 工人首先捡起一根空的松枝干,从小盒子里摸出最上面的一片松针 —— 如果小盒子是空的,就从推送器上取一片松针。将这片松针插到枝干的最下面。
- 工人在插后面的松针时,需要保证,每一步插到一根非空松枝干上的松针片,不能比前一步插上的松针片大。如果小盒子中最上面的松针满足要求,就取之插好;否则去推送器上取一片。如果推送器上拿到的仍然不满足要求,就把拿到的这片堆放到小盒子里,继续去推送器上取下一片。注意这里假设小盒子里的松针片是按放入的顺序堆叠起来的,工人每次只能取出最上面(即最后放入)的一片。
- 当下列三种情况之一发生时,工人会结束手里的松枝制作,开始做下一个:
(1)小盒子已经满了,但推送器上取到的松针仍然不满足要求。此时将手中的松枝放到成品篮里,推送器上取到的松针压回推送器,开始下一根松枝的制作。
(2)小盒子中最上面的松针不满足要求,但推送器上已经没有松针了。此时将手中的松枝放到成品篮里,开始下一根松枝的制作。
(3)手中的松枝干上已经插满了松针,将之放到成品篮里,开始下一根松枝的制作。
现在给定推送器上顺序传过来的 N 片松针的大小,以及小盒子和松枝的容量,请你编写程序自动列出每根成品松枝的信息。
输入格式:
输入在第一行中给出 3 个正整数:N(≤103),为推送器上松针片的数量;M(≤20)为小盒子能存放的松针片的最大数量;K(≤5)为一根松枝干上能插的松针片的最大数量。
随后一行给出 N 个不超过 100 的正整数,为推送器上顺序推出的松针片的大小。
输出格式:
每支松枝成品的信息占一行,顺序给出自底向上每片松针的大小。数字间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
8 3 4
20 25 15 18 20 18 8 5
输出样例:
20 15
20 18 18 8
25 5
代码:
#include<bits/stdc++.h>
using namespace std;
const int mxn=1e6+10;
const int INF=0x3f3f3f3f;
stack<int> box;
queue<int> p,t;
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int a,i=1;i<=n;i++)
cin>>a,p.push(a);
while(p.size()||box.size())
{
t.push(101);//使松枝放下第一个松针
while(t.size()<=k)
{
if(box.size()&&box.top()<=t.back())
t.push(box.top()),box.pop();
//如果盒子不空,且待取元素符合题意
else if(p.size()&&p.front()<=t.back())
t.push(p.front()),p.pop();
//推送器不为空,且待取元素符合题意
else if(p.size()&&box.size()<m)
box.push(p.front()),p.pop();
//将不合题意元素放置到盒子中
else break;
}
t.pop();//去除第一个无用元素
cout<<t.front();t.pop();
while(t.size())
cout<<" "<<t.front(),t.pop();
cout<<endl;
}
return 0;
}