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
歡迎關注我的公衆號「雞翅編程」,這裏是認真且乖巧的碼農一枚,旨在用心寫好每一篇文章,日常會把筆記彙總成推送更新~