最近寫的文章好像還很多的。那么今天我們來討論NOIP初賽的題型——完善程序。完善程序相對是比較難的題目了。全卷100分,完善程序占了大概26分,占比非常大。如果和英語考試試卷做比較,相當於首字母填空(估計是很多人的噩夢)。這類題型難度很大。本文講一下做類似題目的方法。
不過首先,需要足夠的知識儲備,不然再多技巧也沒用。
第一步:看提示,提示往往有很大的作用。
舉例:NOIP2016第一題。
完善程序: **(讀入整數)**請完善下面的程序,使得程序能夠讀入兩個 int 范圍內的整數, 並將這兩個整數分別輸出,每行一個。(第一、五空 2.5 分,其余 3 分)
輸入的整數之間和前后只會出現空格或者回車。輸入數據保證合法。
看題目我們知道,肯定不會太簡單,會暗藏玄機在里面。本人第一次做題的時候第一反應是高精度。(不過題目已經說明在int范圍了)實際上,我們根據題意,可以大致判斷是快速讀入相關的考察。關於快速讀入,我之前寫過一篇文章,可以看這里來參考一下。鏈接:
https://www.cnblogs.com/jisuanjizhishizatan/p/14977531.html
再看NOIP2007的。
完善程序:
(求字符的逆序)下面的程序的功能是輸入若干行字符串,每輸入一行,就按逆序輸出該行,最后鍵入-1終止程序。請將程序補充完整。
這類題目好像很簡單的水題。逆序輸出,其實就是首尾做交換,到中間位置。設置i,j兩個下標進行交換,循環條件i<=j。
第二步,分析算法。有的題目一下子想不出或者描述比較復雜的話,我們嘗試分析算法。分析算法,根據題目描述或者根據殘缺的代碼進行分析。例如,NOIP2016:
本題采用二分法。對於區間[l, r],我們取中間點 mid 並判斷租用到自行 車的人數能否達到 mid。判斷的過程是利用貪心算法實現的。
根據題目我們可以直接知道是二分和貪心。我們截取一段代碼分析:
while (l <= r) { mid = (l + r) / 2; if(④){ ans = mid; l = mid + 1; }else r = ⑤; }
if中肯定是對一個東西的判斷。根據題意,判斷的內容是租用自行車的人數能否達到mid。所以結果就是check(mid)。
第5空更加簡單,上面l=mid+1,下面r就是mid-1。(對稱性)
第三步,套用模版。如果一些題目,分析算法比較麻煩,我們可以套用一些題目的模版。
例如,NOIP2009放置國王。
完善程序:
(國王放置) 在n*m的棋盤上放置k個國王,要求k個國王互相不攻擊,有多少種不同的放置方法。假設國王放置在第(x,y)格,國王的攻擊的區域是:(x-1,y-1), (x-1,y),(x-1,y+1),(x,y-1),(x,y+1),(x+1,y-1),(x+1,y),(x+1,y+1)。讀入三個數n,m,k,輸出答案。題目利用回溯法求解。棋盤行標號為0~n-1,列標號為0~m-1。
看代碼開頭,知道是遞歸回溯法的模版。
CCF基礎篇,這里我把對應的模版格式寫下來了。
再例如,NOIP2008找第k大,直接二分模版。
完善程序:
(找第k大的數) 給定一個長度為1,000,000的無序正整數序列, 以及另一個數n (1<=n<=1000000), 然后以類似快速排序的方法找到序列中第n大的數(關於第n大的數:例如序列{1,2,3,4,5,6}中第3大的數是4)。
大概就是這個樣子。
第四步,分析空的含義。
第一類:初始化。NOIP2009:
int main(){ cin >> n; for (i=1;i<=n;i++) cin >> a[i]; tmp=0; ans=0; len=0; beg= [ ① ] ;
我們就看這段,很明顯是初始化,beg=0直接完成。
第二類,直接調用函數。NOIP2007第一題:
while(【③】) { cin >> line; 【④】;
第4空,是反轉的,直接調用上面編寫的reverse即可。
第三類,上下文推斷。
NOIP2009第一題:
for (i=1;i<=n;i++){ if (tmp+a[i]>ans){ ans=tmp+a[i]; len=i-beg; } else if ( [ ② ] &&i-beg>len) len=i-beg;
下面比上面少了ans=tmp+a[i],不需要賦值,並且根據后文推斷出這一段並不是tmp+a[i]<ans的(如果是小於就要重置beg和tmp),因此這題填tmp+a[i]=ans。
NOIP2015打印日歷:
for (i = 1; i <= ③; i++) { cout << ④; if (i == dayNum[m] || ⑤ == 0) cout << endl; else cout << '\t'; }
我們知道,每次輸出的是日期。輸入月份,輸出的應該依次1到daynum[m]作為日期。
什么情況需要換行?我們知道,7天換一次行,因此是i%7。
如果你這樣想,就錯了!!!在月初,有一些空格。(請注意1前面4個空格)
空格的數量記錄在哪里呢?是記錄在offset變量的。因此,是(offset+i)%7。
然后我們看NOIP2015中位數。
mid = (lbound + rbound) / 2; ②; for (i = 0; i < n; i++) if (③) ④; if (count > n / 2) lbound = mid + 1; else ⑤;
第二空是初始化類型,直接count=0。
第三空是上下文推斷。下文有count,只有可能在這一語句里面出現count相關的東西。因此,推測第四空count++。
什么情況下count要++?這題求中位數,在之后要根據count的值在n的多少繼續二分。我們假設,繼續二分找中位數的區間在右邊(從mid+1到rbound),說明count>n/2,一半以上的數小於中位數,剛好與count>n/2對應(一半以上的數小於count),因此這一空是x[i]>=mid。
第四類,對稱性
也就是上面2015沒有講完的第五空,上面是mid+1,下面就是mid(注意此題比較特殊,不是mid-1,我就被坑過)很多二分的題目都是這樣的。
全文完。