19. 填坑Ⅰ
成绩 10 开启时间 2020年09月17日 星期四 12:00 折扣 0.8 折扣时间 2020年09月24日 星期四 12:00 容许迟交 否 关闭时间 2020年10月10日 星期六 23:00 Descriptionc++
又是北湖深坑,惊不惊喜,意不意外?!程序员
Rack以为用水填湖太没意思了,用石头填坑多有意思。假设北湖的地面仍是一维的,每一块宽度都为1,高度是非负整数,用一个数组来表示。现提供不限量的1x2规格的石头,问是否能够将北湖填平。(全部地面到达同一高度即为填平)算法
注:石头只能水平或垂直填放。编程
Input数组
样例有多组输入至文件末尾;每组用例占两行;数据结构
第一行输入1个整数n表示北湖地面总宽度;函数
第二行输入n个整数用空格间隔,表示地面高度。测试
Outputspa
若能填平则输出“YES”,不然输出“NO”。.net
测试输入 期待的输出 时间限制 内存限制 额外进程 测试用例 1
- 5↵
- 2 1 1 2 5↵
- 3↵
- 4 5 3↵
- 3↵
- 1 2 3↵
- YES↵
- YES↵
- NO↵
1秒 64M 0
1、预热知识
在说具体的算法以前,首先咱们要知道栈这个数据结构,以前在 “括号匹配” 里我已经简单提到过栈。关于栈,你如今只须要知道两点
一、它的特色:单口出入,先进后出。
二、它的使用
注意,小学期你直接学会c++封装的STL的使用便可,至于它是怎么实现的,下学期数据结构这门课有你学的。
#include <stack> //导入库 using namespace std; //肯定命名空间 stack<int> s; //定义一个元素是整型的栈,命名为s int a; s.push(a); //将整型变量压入栈顶 s.size(); //返回栈内元素个数 s.empty(); //返回栈是否为空 /* 注意,如下的操做必须先保证栈不为空,不然会re */ int b = s.top(); //查看栈顶元素,存入变量b中 s.pop(); //出栈:栈顶元素弹出(注意,这个函数返回值为空)
若是你学有余力,也能够看看鸡翅总结的栈的数据结构笔记,贴心传送门:
2、算法分析
下面来分析一下这道题:
用1x2的石头填坑,应该这样理解:能够每次将单位长度升高2米,或者能够将连续两个单位长度同时增加1米。
因为咱们最后的目的就填平至任意整数高度。那么对于两个同高的相邻地方,他们绝对是不会影响咱们的结果的。由于咱们能够一米一米地对他俩增高,到任意咱们想要的高度。故两两相同高度的地方咱们能够直接忽略!好比高度1 2 2 3,咱们能够直接视为1 3相邻,忽略其中两个。因此相邻的定义就放大了。(这里必定要弄明白,这是整个思路的核心,若是不明白就看看后面的再仔细想一想)
那么咱们的思路就出来了,下面是最清楚的思路,其余方法均可以等价地替换成这个方法的处理。因此如下的方式能够正确地解决判断问题:
Ⅰ、 首先找到最高点。
Ⅱ、 将石头以1为底2为高来使用,咱们尽可能将每一处升高到最高点处:
- 与最高点相差奇数个高度,能够填至与最高处只相差一米。记为1
- 与最高点相差偶数个高度,能够刚好填至与最高处齐平。记为0
Ⅲ、而后依次遍历记录下来的整个01串,有相同的在一块儿能够两两相消,若是最后01串内元素大于1,那么说明北湖永远填不平喽!
你不能理解的就是为啥能够两两相消吧,看看以下例子(测试用例1:2 1 1 2 5)。咱们先让每一列贴近最高点,最后的01串为10010。接着能够消成110,接着能够消成0,因此最后输出YES
认真理解一下二、3列为何不影响最后的结果,咱们能够直接将二、3列抵消掉?你想一想咱们怎么再在以下基础上填平,想好了你估计能明白,由于二、3它们能够以1为单位配合到任何高度,不影响咱们的结果,能够直接忽略!!!这里有点只可意会的感受,你若是想不通就多想几个用例....
3、算法实现
你如今应该想到了算法应该怎么实现吧!先找到最高点,而后依次记录每一点与最高点相差高度的奇偶性,奇数记为1,偶数记为0。而后讨论这个01数组...而后得出结果....前面的思路都是对的,而若是你用数组这个数据结构储存,到最后去处理数组里存储的01串结果,就有点繁琐了...可是!你有没有想到你没有用到栈诶!有更加简单处理01串的办法!用栈!
你想一想栈的特色要怎么应用到这个题里呢!栈每次只能访问和处理栈顶的元素,若是咱们依次讨论并将相同的0/1消掉,是否是栈就特别合适呢!这么说好抽象,栈这种微妙的做用,你仔细看看例子来理解:
好了,下面给出完整AC代码:
不对,再另外提几嘴。别嫌我啰嗦,说不定给你减小几小时debug的时间。
一、scanf返回值的问题
不知道你有没有注意过,乐学提交题目常常会给你返回这样一个warning。(虽然咱们程序员常常忽略warning只在意error)
这就是字面意思,你忽略了scanf函数的返回值!什么?我用了这么久的scanf函数还有返回值!?我历来没有用过啊,它不是输入的吗!但是你可能忘了c里全部函数都有设定返回值(void为空)。从这里咱们能够看到scanf返回值为int类型。什么意思呢?这个会返回一个是否成功输入的标识,具体的我就不说了。当 c 在处理输入时,一一读取文件内容,当每有能够读取的时候,scanf的返回值就是EOF。 EOF是一个C标准库定义的常量,不知道你是否还依稀记得 C 语言里把常量用大写字母来标识。(就好比下面代码我就定义了一个常量MAXN,只不过是宏定义的形式哈...)
二、循环输入必须注意
这道题又是循环处理输入的一个题,不少朋友会先写出通常的处理代码再套上循环。或许你也苦恼过为啥我运行过没有问题呀,再次运行就不对了呢?这种的多样例测试必定要注意,若是你没有在每次循环前将变量都初始化,你颇有可能后面一直在反复在以前的基础上使用变量!最好的检测方式就是输入两次同样的用例,看看结果是否相同,若是不一样,那么恭喜你中招了!
因此每定义一个变量就对它初始化这是一个很棒的编程习惯。基本数据类型好比int,double之类的我就不说了,数组咱们一般用string.h或cstring类中的memset函数进行初始化,栈咱们通常在使用前要初始化为空!
这里的易错点就是,你没有每次把栈清空!!后面的运行就会以上一次栈内剩余元素打底。
#include<cstdio> #include<cstring> #include<stack> using namespace std; #define MAXN 200010 long long int a[MAXN] = {0}; stack<int> stk; int main() { long long int n; //当输入没有停止的时候,读入 n while (EOF != scanf("%lld", &n)) { /* 初始化 */ int maxElement_index = 0; //记录对高点的下标 memset(a, 0, sizeof(a[0])); //将数组a全都赋值为 0 while (!stk.empty()) //清空栈 stk.pop(); /* 处理输入 */ for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); //找出最高点下标 if (a[i] > a[maxElement_index]) maxElement_index = i; } /* 核心算法部分 */ int temp; for (int i = 1; i <= n; i++) { if ((a[maxElement_index] - a[i]) % 2 == 1) //差的高为奇数 temp = 0; else //差的高为偶数 temp = 1; if (!stk.empty() && temp == stk.top()) //栈非空的前提下:与前一个能够对应相消时 stk.pop(); else stk.push(temp); } /* 处理结果 */ int remainCount = stk.size(); //计算栈内剩余元素 if (remainCount <= 1) printf("YES\n"); else printf("NO\n"); } return 0; }
End
欢迎关注我的公众号“鸡翅编程”,这里是认真且乖巧的码农一枚,旨在用心写好每一篇文章,日常会把笔记汇总成推送更新~