2022 icpc济南站 K. Stack Sort

题目大意:

有t个样例,每个样例有两行,第一行为n,第二行为从1到n的n个数字,随机排列。

现在要把这n个数字按照他给出的顺序放入m个栈中,要求可以对这m个栈进行pop操作,可以以此取出1,2,3...到n。特别的,如果你开始对第i个栈进行pop操作,那么在这个栈被pop空之前你不可以pop别的栈。

现在要求能满足这个条件最少需要栈的个数m。

解析:

这是这一年的签到题,练习时队友直接自己解决了,回来补题时我才开始看这道题

看到这个题瞬间就想起来了天梯赛的列车调度的题,题目如下:


题目:

火车站的列车调度铁轨的结构如下图所示。

两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有9趟列车,在入口处按照{8,4,2,5,3,9,1,6,7}的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?

输入格式

输入第一行给出一个整数N (2 ≤ N ≤105),下一行给出从1到N的整数序号的一个重排列。数字间以空格分隔。

输出格式:

在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。

不妨先看一下列车调度这道题,可以给stack sort一些启发:

解析:

每条轨道可以把它当成一个队列,先进先出。只需解决以下3个问题即可:

1.避免列车轨道拥堵:

题目要求要先驶出标号大的,再驶出标号小的,那么如果一个火车轨道,标号小的火车停在标号大的火车前面,那么这个列车轨道就会拥堵:比如下图

下面这个轨道先进去了火车4,又进去了火车6,如果4要出去,就要6先出去。但是6又被4堵住出不去,这就是轨道拥堵的情况。

总结一下就是每个轨道中进去的火车的序号的顺序需要是递j减的。

当你访问到序号为i的火车时,如果能找到队尾的序号比i大的轨道,那么把i放进去就不会造成拥堵。相反,如果队尾比i小,那么就会形成上述拥堵的情况。如果没有找到满足条件的轨道,就需要新建一个轨道来存放i了。

2.如何才能让火车轨道最少(贪心)

这里用到了贪心的思想:既然加入一个轨道的前提是需要这个轨道的队尾比i小,那么如果有两条轨道,队尾分别是i-1和i-3,我们应该把i加入到哪一条轨道上呢?

答案显然是放到队尾为i-1的轨道上,因为如果后续还有一个火车序号为i-2,它可以放到队尾为i-3的轨道里面,但是如果把i放入队尾为i-3的轨道中,那么这个轨道的队尾就会变成i,这样i-3可以放到哪呢?没准就没有满足条件的轨道,需要新开辟一条轨道了,这显然不是我们所希望的。

所以,要把标号为i的火车放入到队尾比i大,并且尽可能接近i的轨道中。

3.如何模拟这个过程

用set存放每一个轨道的队尾的序号

标号为i的火车进来,用lower_bound()函数找比第一个比i大数(这里假设为j),把i放入它所在的队列中,因为我们只关心队尾,所以更新队尾即可,队尾从j变成了i,因此我们只需earse(j),insert(i)就完成了一次更新操作。如果没有找到队尾比i大的队列,就新建一个队列,把i放进去,那么i自然而然就是新的队列的队尾,所以只要insert(i)即可。

代码:

#include <iostream>
#include<set>
using namespace std;
const int N = 1e5 + 10;
int  n;
set<int>s;
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		set<int>::iterator it = s.lower_bound(x);
		if (it != s.end())//找到
		{
			s.erase(it);
			s.insert(x);
		}
		else//找不到
			s.insert(x);
	}
	cout << s.size() << endl;
	return 0;
}

回到这个问题上来,这个问题的区别在于

1.这道题模拟的是栈,火车调度模拟的是队列

因此需要用set模拟栈顶而非队尾

2.这道题要求从小到大出栈,火车调度要求从大到小出队列

由于栈和队列出栈的方式相反,这道题要求出容器的顺序相反,这两个相互抵消,导致用set模拟栈顶的时候,也是只要栈顶元素大于i,就不会造成拥堵:

比如这个就会拥堵,1要先出去才能出2,但是1又被2堵上了。

3.一旦一个栈pop了,就要把这个栈pop空才能pop其他的栈

这个区别才是解题的关键,也就是说栈内的元素不仅要从栈顶到下是递增的,还要是连续的。

因此,我们访问到元素i时,需要找的不是有没有栈顶元素比i大,并且尽可能接近i的栈,而是要找有没有栈顶元素为i+1的栈。如果没有,就新建一个栈

代码:

#include<iostream>
#include<set>
using namespace std;
void solve()
{
	set<int> s;
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		int t;
		cin>>t;
		if(s.find(t+1)!=s.end())
			s.erase(t+1);
		s.insert(t);
	}
	cout<<s.size()<<'\n';
}
int main()
{
	int t;
	cin>>t;
	while(t--)
		solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值