解题报告-P6099 [USACO19FEB] Dishwashing G

P6099 [USACO19FEB] Dishwashing G

题目背景

Bessie 和 Elsie 正在帮助 Farmer John 洗碗,这是一个比人们想象的更复杂的过程。

题目描述

两头奶牛决定 Bessie 负责涂肥皂,Elsie 负责冲洗。

刚开始的时候,\(N\) 个脏盘子(保证是从 \(1\)\(N\) 的一个排列)堆在 Bessie 那里,而 Elsie 这边的堆是空的。而在她们俩之间,则有一张专门放涂过肥皂的盘子的桌子。

每个冲洗步骤需要执行以下两个操作之一:

  • Bessie 从脏盘子堆顶取出一个盘子,涂上肥皂,然后放在桌子上。将这个盘子放在桌子上时,Bessie 只能放在现有的非空盘堆的顶端,或是在最右边新增一个盘堆。
  • Elsie 从桌子最左边的盘堆的顶端拿起盘子,将它冲洗后放在干净的盘堆顶端。

她们希望干净的盘堆能按编号排序,编号最小的在底端,编号最大的在顶端。然而她们发现有的时候这并不可能做到。现在给定脏盘子的堆叠顺序,请你求出一个最大前缀,使得该前缀的所有盘子洗干净后,能按上面的要求堆叠。

输入格式

第一行一个整数 \(N\)\(1 \leq N \leq 10^5\))。

接下来 \(N\) 行,每行一个整数,代表 Bessie 的脏盘子堆的堆叠顺序。输入的第一个盘子在堆的顶部。

输出格式

输出该序列的最大前缀长度,使得该前缀的所有盘子洗干净后,能按小号在下,大号在上的规则堆叠。

输入输出样例 #1

输入 #1

5
4
5
2
3
1

输出 #1

4

解题报告

题目大意:

给定 n 个数,从1到 n

每次可以有两种操作:

  • 把第 i 个数放到中转栈里:
    • 可以放入一个已有的栈的顶端。
    • 可以在最右边创建一个新的栈,把这个数放到新的栈里边。
  • 取出最左边的栈里的最顶上的数,放到答案数组中。

求原数组的最长前缀的长度,使这个前缀可以使答案数组递增。

其实对我来讲没那么简单……

按照取盘的顺序,这一题其实需要维护两个条件:

  • 每一个中转栈从栈顶到栈底是递增的(就是单调栈)。
  • \(\forall i \in [2,n]\),中转栈 \(i-1\) 的栈底小于中转栈 \(i\) 的栈顶。

那么我们只需要在处理 \(i\) 时维护好这两个条件就好了。

具体的,对于的 \(i\) 个数 \(x_i\)

  • 如果 \(x_i\) 小于答案数组的最后一位,那么输出最终答案 \(i-1\)
  • 如果 \(x_i\) 比最后一个中转栈的栈底还大,那就新建一个中转栈。
  • 否则就二分出第一个栈底比 \(x_i\) 大的中转栈 \(pos\),把中转栈 \(pos\)\(x_i\) 小的部分全部弹出到答案数组,再把 \(x_i\) 插入到栈 \(pos\) 中。

这实际上是一个贪心策略。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=101100;

#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )

inline int read()
{
	int f=1,x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }
	return f*x;
}

deque<int> z[N];
int top[N],tot;
int n;


signed main()
{
	// freopen("P6099.in","r",stdin);
	// freopen("P6099.out","w",stdout);
    n=read();

    int lst=0;
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(x<lst){ printf("%d\n",i-1);break; }
        if(!tot || x>z[tot].front())
        {
            z[++tot].push_back(x);
            top[tot]=x;
        }
        else
        {
            int pos=upper_bound(top+1,top+tot+1,x)-top;
            while(!z[pos].empty() && z[pos].back()<x)
              lst=z[pos].back(),z[pos].pop_back();
            z[pos].push_back(x);
            top[pos]=z[pos].front();
        }
    }
	return 0;
}
posted @ 2025-09-28 16:29  南北天球  阅读(14)  评论(0)    收藏  举报