一、题目描述
Description
假设北湖的地面是一维的,每一块宽度都为1,高度是非负整数,用一个数组来表示。
现提供不限量的 1 × 2 规格的石头,问是否可以将北湖填平。(所有地面到达同一高度即为填平)
注:石头只能水平或垂直填放。
Input
样例有多组输入至文件末尾;每组用例占两行;
第一行输入1个整数 n (1 ≤ n ≤ 2×1e5 )表示北湖地面总宽度;
第二行输入 n 个整数 a( 0 ≤ a ≤ 1e9 ) ,用空格间隔,表示地面高度。
Output
若能填平则输出“YES”,否则输出“NO”。
测试输入 期待的输出 时间限制 内存限制 额外进程 测试用例 1 以文本方式显示
- 5↵
- 2 1 1 2 5↵
- 3↵
- 4 5 3↵
- 3↵
- 1 2 3↵
以文本方式显示
- YES↵
- YES↵
- NO↵
1秒 64M 0
二、思路过程
需要考虑的主要问题就是:怎么填平?
因为题目给出的样例数太少,为了思考这个问题,首先我是自己用excel做了几个简单的样例,然后自己试着能不能填平,填着填着就发现有几种最简单最容易的情形。
假设我们会垫高到的最终高度是top_h,我们的当前高度是h。
情形1:
当top_h - h是偶数时,直接竖着垫高即可。
情形2:
当h1和h2相邻,而且h1=h2时,可以横着放,把两块同时垫高。
情形3:
当h1和h2相邻,而且h1和h2相差偶数个高度时,先竖着垫到一样高,再横着同时垫高。
于是,我产生的第一个想法就是,我可以把北湖划分为若干个列组,每个列组有一个或两个列,并且每个列组都是上述三种最简单的情形之一。
不过我很快找到了反例,例如
如果按我刚刚的想法,那就被划分成了1,2,3,45,6,7,8,这个图是无解的。然而从图中我们很容易知道实际上是可以填平的,这个算法有极大的漏洞。
但是观察上面的图,左边和右边奇偶性是一一对应的,这才让我们想到了使用栈。
还记得我刚刚说到的情形中有提到:当相差偶数个时,可以填平。其实这道题也就是从这个角度切入的。
我们建立一个栈,每次读入一个元素时。如果栈内为空,或者栈顶元素和该元素奇偶性不同,则入栈;如果栈顶元素和该元素奇偶性相同,则出栈。
最后只要判断,如果栈内的元素个数小于等于1,则输出“YES”,否则输出“NO”。
这个方法还有个好处是只要扫描一次。
三、代码实现
这个思路一旦想到,寥寥几行就完事了。
//总共有n列
for (int i=0;i<n;i++) {
scanf( "%d" , &h );
if ( ( st.size()>0 ) && ( (h+st.top())%2==0 ) ) {
st.pop();
}
else {
st.push(h);
}
}
//然后输出
if (st.size()<=1)
printf("YES\n");
else
printf("NO\n");
四、完整代码
代码完全不难,采用扫描一次的方式也根本不可能超时,这就是栈(队列)的优点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4+10;
int height[N];
struct Stack {
int front = 0;
void push( int h ) {
if (front+1<N) height[front++] = h;
}
void pop() {
if (front>0) front--;
}
int top() {
if (front>0) return height[front-1];
}
int size() {
return front;
}
};
int main(){
int n,h;
while ( scanf("%d",&n) != EOF ) {
Stack st;
for (int i=0;i<n;i++) {
scanf("%d",&h);
if ( ( st.size()>0 ) && ( (h+st.top())%2==0 ) ) {
st.pop();
}
else {
st.push(h);
}
}
if (st.size()<=1) printf("YES\n");
else printf("NO\n");
}
return 0;
}