NOIP初賽:完善程序做題技巧


最近寫的文章好像還很多的。那么今天我們來討論NOIP初賽的題型——完善程序。完善程序相對是比較難的題目了。全卷100分,完善程序占了大概26分,占比非常大。如果和英語考試試卷做比較,相當於首字母填空(估計是很多人的噩夢)。這類題型難度很大。本文講一下做類似題目的方法。

不過首先,需要足夠的知識儲備,不然再多技巧也沒用。

第一步:看提示,提示往往有很大的作用。

 

舉例:NOIP2016第一題。

完善程序: **(讀入整數)**請完善下面的程序,使得程序能夠讀入兩個 int 范圍內的整數, 並將這兩個整數分別輸出,每行一個。(第一、五空 2.5 分,其余 分)
輸入的整數之間和前后只會出現空格或者回車。輸入數據保證合法。

 

看題目我們知道,肯定不會太簡單,會暗藏玄機在里面。本人第一次做題的時候第一反應是高精度。(不過題目已經說明在int范圍了)實際上,我們根據題意,可以大致判斷是快速讀入相關的考察。關於快速讀入,我之前寫過一篇文章,可以看這里來參考一下。鏈接:

https://www.cnblogs.com/jisuanjizhishizatan/p/14977531.html

 

再看NOIP2007的。

完善程序:
(求字符的逆序)下面的程序的功能是輸入若干行字符串,每輸入一行,就按逆序輸出該行,最后鍵入-1終止程序。請將程序補充完整。

 

這類題目好像很簡單的水題。逆序輸出,其實就是首尾做交換,到中間位置。設置ij兩個下標進行交換,循環條件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大的數:例如序列{123456}中第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的(如果是小於就要重置begtmp),因此這題填tmp+a[i]=ans。

NOIP2015打印日歷:

 

for (i = 1; i <= ③; i++)
    {
        cout << ④;
        if (i == dayNum[m] || ⑤ == 0)
            cout << endl;
        else
            cout << '\t';
    }

 

我們知道,每次輸出的是日期。輸入月份,輸出的應該依次1daynum[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+1rbound),說明count>n/2,一半以上的數小於中位數,剛好與count>n/2對應(一半以上的數小於count),因此這一空是x[i]>=mid。

 

第四類,對稱性

也就是上面2015沒有講完的第五空,上面是mid+1,下面就是mid(注意此題比較特殊,不是mid-1,我就被坑過)很多二分的題目都是這樣的。

 

全文完。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM