Dishwashing (USACO Gold 2019 February)
此题在洛谷上难度标识很低(普及),但还是需要一定的思考的,至少应该是提高难度吧。
初步观察,Bessie能做的事情是比较有限的,可以在现有堆的顶上方盘子,或者新开堆,但是加入Elsie之后,组合就变多了,Elsie一方面可以从最左侧顶端拿盘子,改变最左侧堆的后续顺序。而如果她把最左侧堆全部拿完,则就可以把右面的堆变成最左侧的堆。这样就发现组合很多,不好想。
进一步观察之后,我们发现在这样的规则下,堆的之间有这样一些规律:
- 首先所谓的堆其实是个栈(stack),先进后出。
- 关键规律是:多个堆从左往右,底部盘子号上升,从下往上盘子号下降,只有这样才能使得最后单调上升。
- 所以来一个新盘子,通过底部盘子可以确定要放在哪个堆,如果比所有底部盘子大,则开新的堆。
- 如果发现比应该在的栈的顶部盘子号码大,那唯一的办法就是把这个stack左侧所有盘子,以及这个栈顶的盘子由Elsie洗干净(pop)掉,记录下洗干净的盘子的最大号码,然后这个盘子可以放置在这个栈(已经是新的最左的栈),并保持从下到上号码下降。
- 如果发现新来的盘子号码比洗干净过的盘子小,则不能再放置,过程结束。
所以总体上就是一个规则多一点的模拟过程,程序实现简单,但思维过程还是有点绕的,题目出得挺好。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n; // <= 1e5
vector<int> bottoms;
vector<stack<int>> st;
int rinsed;
int ans;
int main() {
freopen("dishes.in", "r", stdin);
freopen("dishes.out", "w", stdout);
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int a;
scanf("%d", &a);
int j = lower_bound(bottoms.begin(), bottoms.end(), a) - bottoms.begin();
if (j == bottoms.size()) { // new soapy stack
bottoms.push_back(a);
stack<int> s;
s.push(a);
st.push_back(s);
ans = i+1;
continue;
}
if (a < rinsed) break; // cannot place plate lower than last rinsed
while (a > st[j].top()) { // rinse plates
rinsed = st[j].top();
st[j].pop();
}
st[j].push(a); // place soapy plate
ans = i+1;
}
printf("%d", ans);
return 0;
}