【BIT2021程设】18.填坑I

本文作者分享了如何解决一道程序设计题,该题涉及填湖策略,通过使用类似俄罗斯方块的思路,用12方块填平地面,并利用栈的数据结构来优化处理过程。作者提供了伪代码并分析了问题的关键在于消除相邻的0或1,证明了栈顶消去的有效性。最终,通过判断栈的大小来确定是否能填平湖面。
摘要由CSDN通过智能技术生成

写在前面:

本系列博客仅作为本人十一假期过于无聊的产物,对小学期的程序设计作业进行一个总结式的回顾,如果将来有BIT的学弟学妹们在百度搜思路时翻到了这一条博客,也希望它能对你产生一点帮助(当然,依经验来看,每年的题目也会有些许的不同,所以不能保证每一题都覆盖到,还请见谅)。

不过本人由于学艺不精,代码定有许多不足之处,欢迎各位一同来探讨。

同时请未来浏览这条博客的学弟学妹们注意,对于我给出完整代码的这些题,仅作帮助大家理解思路所用(当然,因为懒,所以大部分题我都只给一个伪代码)。Anyway,请勿直接复制黏贴代码,小学期的作业也是要查重的,一旦被查到代码重复会严厉扣分,最好的方法是浏览一遍代码并且掌握相关的要领后自己手打一遍,同时也要做好总结和回顾的工作,这样才能高效地提升自己的代码水平。

加油!


成绩10开启时间2021年09月3日 星期五 11:00
折扣0.8折扣时间2021年09月10日 星期五 23:00
允许迟交关闭时间2021年10月10日 星期日 23:00

Description

又是北湖深坑,惊不惊喜,意不意外?!

Roark觉得用水填湖太没意思了,用石头填坑多有意思。

假设北湖的地面还是一维的,每一块宽度都为1,高度是非负整数,用一个数组来表示。

现提供不限量的1\times 2规格的石头,问是否可以将北湖填平。(所有地面到达同一高度即为填平)

注:石头只能水平或垂直填放。

Input

样例有多组输入至文件末尾;

每组用例占两行;

第一行输入1个整数 n(1\leq n\leq 2e5)表示北湖地面总宽度;

第二行输入n个数a_i(0\leq a_i\leq 1e9) ,用空格间隔,表示地面高度。

Output

若能填平则输出“YES”,否则输出“NO”。

测试用例 1以文本方式显示
  1. 5↵
  2. 2 1 1 2 5↵
  3. 3↵
  4. 4 5 3↵
  5. 3↵
  6. 1 2 3↵
以文本方式显示
  1. YES↵
  2. YES↵
  3. NO↵
1秒64M

题意分析:

        考查优化策略的一道题。

        对于这种既能横放又能竖放的,一通瞎操作随便放肯定不行,暴力搜索也肯定不行,所以必然有优化方法。既然每个地方都给出了“高度”,那这个图像就跟俄罗斯方块差不多——唯一的区别就是,不会出现上面有顶下面有底而中间是空的情况。玩俄罗斯方块你最喜欢啥呀?一定是那个1\times 4的棍子,因为他完全不用考虑地形如何,只要竖着放不碰到顶,最起码这一步是没有危险的(没有形成中空区域的风险)。于是这道题我们也可以类似的思考,既然他没说要我们用最少的石头来填,那我们就可劲填,先用竖着的1\times2方块把每个地方都填到“近似”相同高度为止。

         不难用数学证明出来,这样填完之后的结果,一定是像“锯齿”一样的序列,高度极差最多为1,如下图所示:

        这样,其实整个信息就被简化为了一个01序列,我们要研究的就是,当这个01序列满足什么样的性质的时候,它能够被完全的填平。

        首先我们先看这个01序列本身有什么性质吧,最重要的一点,是如果对这个序列各位取反,和这个序列本身是等价的,这个的证明非常显而易见,我们只要把每个“0”的位置上竖着放一个 1\times2方块,它就变成了2,于是每个0都变成了2,每个1都不变,如果全体减一,相当于对原本那个01序列取反。

        如果这个序列中出现了相邻的0或者相邻的1,那在我们眼里是再好不过的事,因为这样他们就可以水平地放置 2\times1的方块,这样高度就可以以1位单位增加——换句话说,想叠到什么高度,就能叠到什么高度,因此如果遇到了这样的子段,我们完全可以不管它。

        但是“不管”也分情况,是直接当这俩不存在呢,还是在遍历的时候跳过呢?我们就要研究另一种情况,即类似1001这样的序列——如果直接当这俩不存在,那么00消去,11也变成了相邻的序列,也能消去,这样可行吗?答案是肯定的,因为我们一旦把中间填上一个2\times1的水平方块,整个序列就变成了了1111,也就是0000,那么就是两组相邻的0,自然可以消去。

        类似的,我们可以证明一个结论,每当出现相邻的0或者相邻的1时,我们都可以直接消去。这叫什么呀?这就是栈顶消去呀,这就是后进先出LIFO呀,那用个栈就非常的水到渠成了。

        于是,到目前为止的思路就出来了,参见下方伪代码。(当然,这个伪代码有非常多可以优化的空间,有余力的同学可以自己思考一下)


伪代码:

读入数据组数,按组循环:

        长度为n的循环:       

                读入数据,将每个数据&1,得到这个数据的二进制编码;

                //想一想,&1是什么意思

                二进制编码入栈;

                循环条件:如果栈顶有两个相同编码

                        连续弹出栈顶两次

        如果最后栈为空或栈大小为1,输出YES,否则输出NO;

        //想一想,为什么栈为1也可以

 贴代码:

#include <bits/stdc++.h>  
#define pb push_back  
#define pf push_front  
#define __MAX 100010  
using namespace std;  
typedef long long ll;  
typedef unsigned long long ull;  
typedef pair<int, int> pii;  
typedef stack<int> stk;  
typedef unordered_map<int, int> umi;  
typedef unordered_set<int> usi;  
const int INF = 0x3f3f3f;  
const double EPS = 1e-8;  
const double PI = acos(-1);  
  
  
int h[200010];  
int c = 0;  
  
int main(){  
    ///ifstream infile("input.txt", ios::in);  
    ///ofstream outfile("output.txt", ios::out);  
  
    int width;  
    while(cin >> width){  
        int max = 0;  
        for(int i = 0; i < width; i++){  
            scanf("%d", &h[i]);  
            if(h[i] > max)  
                max = h[i];  
        }  
        for(int i = 0; i < width; i++){  
            if((max - h[i]) % 2 == 0)   //这里不要学我这么写!!非常的慢!直接位运算就行
                h[i] = 0;  
            else  
                h[i] = 1;  
        }  
        stk s;  
        for(int i = 0; i < width; i++){  
            if(s.empty()){  
                s.push(h[i]);  
            } else{  
                if(s.top() == h[i])  
                    s.pop();  
                else{  
                    s.push(h[i]);  
                }  
            }  
        }  
  
        if(s.size() <= 1)  
            cout << "YES" << endl;  
        else  
            cout << "NO" << endl;  
  
  
  
  
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千里之码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值