1. 程式人生 > >【用腳趾頭解決NOIP】(2)NOIP2009~NOIP2015年普及組完善程式

【用腳趾頭解決NOIP】(2)NOIP2009~NOIP2015年普及組完善程式

文章索引:

這一篇部落格,算是我的作業吧。

根據難度與時間,我從完善程式的第一題開始,從NOIP2009到NOIP2015年,逐題寫出思考思想。

NOIP2009(完善程式第一題):最大連續欄位和

題意描述:

給出一個數列(元素個數不多於100),數列元素均為負整數、正整數、0。請找出數列中的一個連續子數列,使得這個子數列中包含的所有元素之和最大,在和最大的前提下還要求該子數列包含的元素個數最多,並輸出這個最大和以及該連續子數列中元素的個數。

題目原始碼:

#include <cstdio>
#include <iostream>
using namespace std;
int a[101];
int n,i,ans,len,tmp,beg;
int main(){
    cin>>n;
    for(i=1;i<=n;i++){
        cin>>a[i];
    }
    tmp=0;
    ans=0;
    len=0;
    beg=/*空<1>*/;
    for(i=1;i<=n;i++){
        if(tmp+a[i]>ans){
            ans=tmp+a[i];
            len = i-beg;
        }
        else if(/*空<2>*/ && i-beg>len){
            len = i-beg;
        }
        if(tmp+a[i]/*空<3>*/){
            beg=/*空4*/;
            tmp=0;
        }
        else /*空5*/
        cout<<ans<<" "<<len<<endl;
        return 0;
    }
}
解題過程:

空<1>其實很明顯,前面都在初始化,這個beg一起初始化就好了,即beg=0.

空<2>之後的表示式依然將長度擴充,得到現統計結果(tmp+a[i])與原統計結果(ans)相同。即tmp+a[i]==ans

空<3>發現之後的表示式中將目前累計的結果(tmp)清空,題意中提到“所有的元素均為正整數、負整數、0”,所以有出現0的情況,如果最大連續欄位和中出現負數的情況下一定不是最優解,所以這個空是特判和小於0的,即

tmp+a[i]<0(實際上填寫“<0”)

空<4>顯而易見,既然將beg再一次進行賦值,因為從原來到現在的總和出現負數,所以將beg重新設定成i就可以了。即

beg = i(實際上填寫"i")

空<5>就是在tmp+a[i]大於等於0的時候,將計數器累加即可。即tmp+=a[i](或者tmp=tmp+a[i])

(這道題目居然不是用狀態轉移方程來寫,喪病啊!)

下一題。

NOIP2010(完善程式第一題):哥德巴赫猜想

題意描述:

哥德巴赫猜想是指,任一大於2的偶數都可寫成兩個質數之和。迄今為止,這仍然是一個著名的世界難題,被譽為數學王冠上的明珠。試編寫程式,驗證任一大於2且不超過n的偶數都能寫成兩個質數之和。

題目原始碼:

#include <iostream>
using namespace std;

int main(){
    const int SIZE = 1000;
    int n,r,p[SIZE],i,j,k,ans;
    bool tmp;
    cin>>n;
    r=1;
    p[1]=2;
    for(i=3;i<=n;i++){
        /*空<1>*/
        for(j=1;j<=r;j++){
            if(i%/*空<2>*/==0){
                tmp=false;
                break;
            }
        }
        if(tmp){
            r++;
            /*空<3>*/
        }
        
    }
    ans=0;
    for(i=2;i<=n/2;i++){
        tmp = false;
        for(j=1;j<=r;j++){
            for(k=<span style="font-family:FangSong_GB2312;">j</span>;k<=r;k++){
                if(i+i==/*空<4>*/){
                    tmp=true;
                    break;
                }
            }
        }
        if(tmp) ans++;
    }
    cout<<ans<<endl;
}
如果輸入2010,輸出的是  空<5>  

解題過程
空<1>:當前tmp未初始化,直接初始化tmp。即tmp=true

空<2>得知p陣列為素數表,那麼每一次搜尋時掃描素數表中的每一個元素。如果掃描到了該元素的倍數,則標記。

所以填i%p[j]==0(實際上填"p[j]")

空<3>如果找到一個素數,就把r擴充套件,將得到的新元素(i)賦值給目前為空的p[r]中。即p[r] = i

空<4>哥德巴赫猜想:任一大於2的偶數都可寫成兩個質數之和。如果i的兩倍與任意兩個素數和相同,就將tmp設成true,得到這裡需要列舉素數。所以寫成if(i+i==p[j]+p[k])(實際填寫“p[j]+p[k]”)。

空<5>這。。。這不是閱讀程式寫執行結果麼??

不解釋。答案1004

好吧=_=經歷了不知道為什麼完形填空變成閱讀理解完善程式變成讀程式寫執行結果之後,來到了下一題。

NOIP2011(完善程式第一題):子矩陣

題意描述:

給輸入一個n1*m1的矩陣a,和n2*m2的矩陣b,問a中是否存在子矩陣和b相等。若存在,輸出所有子矩陣左上角的座標:若不存在輸出“There is no answer”。

題目原始碼:

#include <iostream>
using namespace std;

const int SIZE = 50;
int n1,m1,n2,m2,a[SIZE][SIZE],b[SIZE][SIZE];

int main(){
    int i,j,k1,k2;
    bool haveAns,good;
    cin>>n1>>m1;
    for(i=1;i<=n1;i++)
        for(j=1;j<=m1;j++)
            cin>>a[i][j];
    cin>>n2>>m2;
    for(i=1;i<=n2;i++) 
        for(j=1;j<=m2;j++)
            /*空<1>*/;
    haveAns = false;
    for(i=1;i<=n1-n2+1;i++){
        for(j=1;j<=/*空<2>*/){
            /*空<3>*/;
            for(k1=1;k1<=n2;k1++){
                for(k2=1;k2<=/*空<4>*/;k2++){
                    if(a[i+k1-1][j+k2-1]!=b[k1][k2]){
                        good = false;
                    }
                }
            }
            if(good){
                cout<<i<<" "<<j<<endl;
                /*空<5>*/;
            }
        }
    }
    if(!haveAns) printf("There is no answer");
    return 0;
} 
</span>
這一道題......

當我打出這道題目的程式碼之後,我無風自凌亂=_=,我只想說:

這已經不能叫水題了,簡直是送分!送分啊!

空<1>輸入...... 答案:cin>>b[i][j]

空<2>直接照搬上面的for迴圈。 答案:m1-m2+1;

空<3>因為good沒有初始化,下面又將它設定為false,得到這裡將good設定為true。即good = true

空<4>照搬...... 答案:m2

空<5>......我說什麼好呢......因為得到答案,將haveAns設為true。答案:haveAns = true
所以......允許我笑一下。

下一題.....

NOIP2012(完善程式第一題):座標統計

題意描述:

輸入n個整點在平面上的座標。對於每個點,可以控制所有位於它左下方的點(即x、y座標都比它小),它可以控制的點的數目稱為“戰鬥力”。依次輸出每個點的戰鬥力,最後輸出戰鬥力最高的點的編號(如果若干個點的戰鬥力並列最高,輸出其中最大的編號)。

題目原始碼:

#include <iostream>
using namespace std;
const int SIZE =100;
int x[SIZE],y[SIZE],f[SIZE];
int n,i,j,max_f,ans;
int main()
{
	cin>>n;
	for(i=1;i<=n;i++) cin>>x[i]>>y[i];
	max_f=0;
	for(i=1;i<=n;i++)
	{
		f[i]=/*空<1>*/;
		for(j=1;j<=n;j++)
		{
			if(x[j]<x[i] &&/*空<2>*/)
			     /*空<3>*/;
		}
		if(/*空<4>*/)
		{
			max_f=f[i];
			/*空5*/ ;	
		}
	}
	for(i=1;i<=n;i++) cout<<f[i]<<endl;
	cout<<ans<<endl;
	return 0;	
}

思路描述:

空<1>:為了防止上一次的統計結果影響到這一次,所以要清0.即f[i] = 0(實際上填寫“0”)

空<2>:因為需要X2,Y2座標都需要小於X1,Y1時,點1才能擁有點2的戰鬥力+1,所以填寫y[j]<y[i]

空<3>:因為控制了當前點,戰鬥力增加。填寫f[i]++或f[i] = f[i]+1

空<4>:......

空<5>:因為如果有多個點戰鬥力相同,就要輸出編號最大的點的編號,所以需要記住當前點的編號,繼續向後(有可能有更大的編號)。又因為輸出的是ans,得到答案:ans = max_f。

(令人欣慰的是:CCF在2012世界末日的時候沒有放棄治療)

NOIP2013(完善程式第一題):序列重排

題意描述:

全域性陣列變數a定義如下:

const int SIZE = 100;
int a[SIZE] , n
它記錄著一個長度為n的序列a[1], a[2], …, a[n]。

現在需要一個函式,以整數p (1 ≤ p ≤ n)為引數,實現如下功能:將序列a的前p個數與後n-p個數對調,且不改變這p個數(或n-p個數)之間的相對位置。

題目原始碼:

/*樸素演算法,時間複雜度 O(n) , 空間複雜度 O(n)*/
void swap1(int p){
    int i,j,b[SIZE];
    for(i=1;i<=p;i++)
        b[/*空<1>*/] = a[i];
    for(i=p+1;i<=n;i++)
        b[i-p] = /*空<2>*/;
    for(i=1;i<=/*空<3>*/;i++)
        a[i] = b[i];
}

/*以時間換空間 , 時間複雜度 O(n^2) , 空間複雜度 O(1)*/

void swap2(int p){
    int i,j,temp;
    for(i=p+1;i<=n;i++){
        temp = a[i];
        for(j=i;j>=/*空<4>*/;j--){
            a[j] = a[j-1];
            /*空<5>*/ = temp;
        }
    }
}
開始解題√

這裡我們需要畫圖了。

設: a[ ] = {1,2,3,4,5} , p=2

初始序列:

空<1>:很明顯這裡是出來p之前(包括p)的元素,將這裡的元素進行替換。我們的目標陣列是:


得到元素位置轉移:1→4,2→5

經過簡單演算後得知:b[n-p+1] = a[i],即正確答案為b[n-p+1]。

空<2>:同樣依照上面的圖片。

元素位置轉移:

3→1 , 4→2 , 5→3

經過演算後得知:b[i] = a[i+p],即b[i-p] = a[i],答案是a[i]。

空<3>:雖然不知道他為什麼要覆蓋a陣列(可能是b陣列還要用),總之就是講b陣列複製到a。所以填寫n

NOIP2014(完善程式第一題):數字刪除

題意描述:

給定一個字串,將字串中的數字字元刪除後輸出。

題目原始碼:

#include <iostream>

int delnum(char *s){
    int i,j;
    j = 0;
    for(i=0; s[i] != '\0' ; i++){
        if(s[i] < '0' /*空<1>*/ s[i] > '9'){
            s[j] = s[i];
            /*空<2>*/;
        }
    }
    return /*空<3>*/;
}

const int SIZE = 30;

int main(){
    char s[SIZE];
    int len , i;
    cin.getline(s , sizeof(s));
    len = delnum(s);
    for(i=0;i<len;i++){
        cout<</*空<4>*/;
    }
    cout<<endl;
    return 0;
}
解題開始:

空<1>:很明顯,這裡是要判斷這個字元是否是數字,即 s[i]<‘0’||s[i]>‘9’;(實際上填寫"||")

空<2>:因為要統計更新後陣列的長度,將j記為陣列長度,這裡更新了陣列,所以長度加1,即j++

空<3>:計算完畢,返回的是陣列長度,即j

空<4>:輸出更新後的s陣列,即cout<<s[i](實際填寫"s[i]")

NOIP2015(完善程式第一題):列印月曆

題意描述:

輸入月份m(1 ≤ m ≤ 12),按一定格式列印2015年第m月的月曆。

題目原始碼:

#include <iostream> 
using namespace std;  
const int dayNum[]={-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 
int m, offset, i;  
int main() {
    cin >> m; 
    cout << "S\tM\tT\tW\tT\tF\tS" << endl;     
    /*空<1>*/
    for (i = 1; i < m; i++) 
        offset =/*空<2>*/;        
    for (i = 0; i < offset; i++)
        cout << '\t'; 
    for (i = 1; i <=/*空<3>*/;i++) {
        cout <</*空<4>*/;  
        if (i == dayNum[m] ||/*空<5>*/== 0)
            cout << endl;
        else 
            cout << '\t';     
    } 
    return 0; 
}
解題思路:

2015年1月月曆奉上


空<1>:offset這個引數控制著每個月前輸出空格的數量。首次設成4.(上面的日曆,如果再將週日放到週一前面,就有4個空),即offset=4.

空<2>:這裡是為了得到每一個月輸出日曆前面輸出的空格數量。這裡有一個神奇的規律。將offset的初始值設定為4後,列印過當月的月曆之後,留下的空就是(offset+當月天數)%7,即(offset+Daynum[i])%7。

空<3>:因為開始列印月曆,所以要迴圈第m月的天數。即for(i=1;i<Daynum[m];i++)。(實際上填寫"Daynum[m]")

空<4>:打印出i。即cout<<i(實際上填寫"i")

空<5>:這裡是控制每一星期後的換行。那麼每一次列印的日期加上offset若取餘7等於0,就可以換行。即(offset+i)%7==0。(實際填寫"offset+i")

第一題全部解題完畢。

============================

接下來對第二題進行解答。

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)

(注:棋盤行標號為0→n-1,列標號為0→m-1)

題目原始碼:

#include <iostream>
using namespace std;

int n,k,ans;
int hash[5][5];

void work(int x,int y,int tot){
    int i,j;
    if(tot==k){
        ans++;
        return;
    }
    do{
        while(hash[x][y]){
            y++;
            if(y==m){
                x++;
                y=/*空<1>*/;
            }
            if(x==n){
                return;
            } 
        }
        for(i=x-1;i<=x+1;i++)
            if(i>=0 && i<n)
                for(j=y-1;j<=y+1;j++)
                    if(j>=0 && j<m)
                        /*空<2>*/;
        /*空<3>*/;
        for(i=x-1;i<=x+1;i++)
            if(i>=0 && i<n)
                for(j=y-1;j<=y+1;j++)
                    if(j>=0 && j<m)
                        /*空<4>*/;
        y++;
        if(y==m){
            x++;
            y=0;
        }
        
    }while(1);
}

int main(){
    cin >> n >> m >> k;
    ans=0;
    memset(hash,0,sizeof(hash));
    /*空<5>*/;
    cout<<ans<<endl;
}
解題思路:

空<1>:因為y搜尋到了最右邊,所以將y清零,x繼續搜尋下去。

空<2>:因為找到了一個可行的解,所以更新hash。即hash[i][j]++;

空<3>:這裡進行遞迴。因為找到可行放置方法,所以遞迴的時候,返回的是更新的x,y,以及tot++。即work(x,y,tot+1);

空<4>遞迴完成,不能結束遞迴。所以將hash重新置為0.這裡是hash[i][j]--;

空<5>開始遞迴。即work(0,0,0)。(因為棋盤的橫座標標號為0~n-1,縱座標標號為0~m-1,所以要從0,0開始更新)

NOIP2010(完善程式第二題):過河問題

題意描述:

在一個月黑風高的夜晚,有一群人在河的右岸,想通過唯一的一根獨木橋走到河的左岸.在伸手不見五指的黑夜裡,過橋時必須借照燈光來照明,不幸的是,他們只有一盞燈.另外,獨木橋上最多能承受兩個人同時經過,否則將會坍塌.每個人單獨過獨木橋都需要一定的時間,不同的人要的時間可能不同.兩個人一起過獨木橋時,由於只有一盞燈,所以需要的時間是較慢的那個人單獨過橋所花費的時間.現在輸入N(2<=N<1000)和這N個人單獨過橋需要的時間,請計算總共最少需要多少時間,他們才能全部到達河左岸.

題目原始碼:

#include <iostream>
#include <cstring>

using namespace std;
 
const int SIZE=100;  
const int INFINITY = 10000; 
const bool LEFT=true; 
const bool RIGHT =false; 
const bool LEFT_TO_RIGHT=true; 
const bool RIGHT_TO_LEFT=false; 

int n,hour[SIZE];
bool pos[SIZE];

int max(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}

int go(bool stage) { 
    int i,j,num,tmp,ans;     
    if(stage==RIGHT_TO_LEFT)     
    { 
        num=0;
        ans=0; 
        for(i=1;i<=n;i++)   
            if(pos[i]==RIGHT){ 
               num++; 
               if(hour[i]>ans)
                  ans=hour[i];            
            } 
        if(/*空<1>*/) return ans;         
        ans=INFINITY;
        for(i=1;i<=n-1;i++)
            if(pos[i]==RIGHT)                
                for(j=i+1;j<=n;j++)
                    if(pos[j]==RIGHT){ 
                      pos[i]=LEFT;
                      pos[j]=LEFT; 
                      tmp=max(hour[i],hour[j])+/*空<2>*/;
                      if(tmp<ans)
                         ans=tmp;                      
                      pos[i]=RIGHT;                       
                      pos[j]=RIGHT;                                          
                    }        
     return ans;     
    }
    if(stage==LEFT_TO_RIGHT){ 
        ans=INFINITY;         
        for(i=1;i<=n;i++) 
            if(/*空<3>*/){ 
                pos[i]=RIGHT; 
                tmp=/*空<4>*/;
                if(tmp<ans)                     
                ans=tmp;
                /*空<5>*/;
            }         
        return ans;     
    } 
    return 0; 
    }  
int main() { 
    int i;     
    cin>>n; 
    for(i=1;i<=n;i++){ 
        cin>>hour[i];         
        pos[i]=RIGHT;     
    }
    cout<<go(RIGHT_TO_LEFT)<<endl;     
    return 0; 
}
解題思路:

空<1>:這個空是一次特判。如果人數只有兩個人或者更少,那麼不需要繼續運算,返回答案即可。即n<=2

空<2>:這裡是人從右向左走的時間判定操作。如果有兩個人向左走,那麼就把這兩個人設成LEFT,表示他們目前在左邊。那麼他們前進的時間就是兩個人中用時多的那個人的用時,但是因為過橋需要燈,所以要有人重新回到右邊。這裡要用到遞迴,執行向右走操作。即go(RIGHT_TO_LEFT)

空<3>:這裡是判定當前選中的人i是否在左邊。如果在,那麼就繼續執行送燈操作。即if(pos[i] == LEFT)

空<4>:這裡是更新tmp,要用當前這個在左邊的人去送燈,所以消耗時間是當前這個人的時間,即hour[i],然後需要進行左走右的操作,即go(LEFT_TO_RIGHT),那麼tmp就是hour[i]+go(LEFT_TO_RIGHT)

空<5>這個人最後需要回到左邊,所以在遞迴結束後將pos[i]設定成LEFT,即pos[i] = LEFT

NOIP2011(完善程式第二題):大整數開方

題意描述:輸入一個大整數,用二分法求出這個數的平方根的整數部分

#include<iostream> #include<string> using namespace std;  
const int SIZE=200; struct hugeint{     int len,num[SIZE]; }; 
//其中len表示大整數的位數;num[1]表示個位,num[2]表示十位,以此類推  
hugeint times(hugeint a,hugeint b){  
// 計算大整數a和b的乘積 
    int i,j;     
    hugeint ans; 
    memset(ans.num,0,sizeof(ans.num));     
    for(i=1;i<=a.len;i++)        
        for(j=1;j<=b.len;j++) 
        /*空<1>*/+=a.num[i]*b.num[j];       
    for(i=1;i<=a.len+b.len;i++){         
        ans.num[i+1]+=ans.num[i]/10;
        /*空<2>*/;      
    } 
    if(ans.num[a.len+b.len]>0)        
        ans.len=a.len+b.len;     
    else 
        ans.len=a.len+b.len-1;     
    return ans; 
}

hugeint add(hugeint a,hugeint b){ //計算大整數a和b 的和 
 
    int i;     
    hugeint ans; 
    memset(ans.num,0,sizeof(ans.num));     
    if(a.len>b.len)         
        ans.len=a.len;     
    else 
        ans.len=b.len; 
    for(i=1;i<=ans.len;i++){ 
        ans.num[i]+=/*空<3>*/;          
        ans.num[i+1]+= ans.num[i]/10;         
        ans.num[i]%=10;     
    } 
    if(ans.num[ans.len+1]>0)         
        ans.len++;     
    return ans; 
}  
hugeint average(hugeint a,hugeint b) { //計算大整數a和b的平均數的整數部分 
    int i;     
    hugeint ans;     
    ans=add(a,b); 
    for(i=ans.len;i>=2;i--){ 
        ans.num[i-1]+=(/*空<4>*/)*10;   
        ans.num[i]/=2;     
    } 
    ans.num[1]/=2; 
    if(ans.num[ans.len]==0)         
        ans.len--;
    return ans; 
}  
hugeint plustwo(hugeint a){ // 計算大整數a加2之後的結果  
    int i;     hugeint ans;     
    ans=a; 
    ans.num[1]+=2;     
    i=1; 
    while( (i<=ans.len)&&(ans.num[i]>=10) ){         
        ans.num[i+1]+=ans.num[i]/10;         
        ans.num[i]%=10;         
        i++;     
    }
    if(ans.num[ans.len+1]>0)
        /*空<5>*/;      
    return ans; 
    }  
bool over(hugeint a,hugeint b) // 若大整數a>b則返回true,否則返回false { 
    int i; 
    if(/*空<6>*/)           
        return false;     
    if( a.len>b.len )         
        return true; 
    for(i=a.len;i>=1;i--){        
         if(a.num[i]<b.num[i])            
            return false;         
         if(a.num[i]>b.num[i])            
            return true;
    return false;
}  
int main() { 
    string s;
    int i; 
    hugeint target,left,middle,right;     
    cin>>s; 
    memset(target.num,0,sizeof(target.num));     
    target.len=s.length();     
    for(i=1;i<=target.len;i++) 
        target.num[i]=s[target.len-i]-/*空<7>*/;     
    memset(left.num,0,sizeof(left.num));     
    left.len=1;     
    left.num[1]=1;     
    right=target;     
    do{ 
        middle=average(left,right);         
        if(over(/*空<8>*/))             
            right=middle;         
        else 
            left=middle; 
    }while(!over(plustwo(left),right) );     
    for(i=left.len;i>=1;i--)        
        cout<<left.num[i];     
    return 0; 
    }
    

空<1>:因為是計算兩數的乘積,所以爆儲存到第i+j-1位中。即ans.num[i+j-1]

空<2>:這裡是進位操作。所以要將這個數對10取餘。即ans.num[i]%=10

空<3>:這裡是加法操作,將a與b的num[i]相加即可。即a.num[i]+b.num[i]

空<4>:因為要求平局數,那麼就要讓這個數取餘2,,即ans.num[i]%2

空<5>:因為在更高位存在數字,那麼將這個數字的長度加1即可。即ans.len++

空<6>:如果b比a長(這樣值更大),就返回false,即a.len<b.len

NOIP2012(完善程式第二題):排列數

題意描述:輸入兩個正整數n,m(1<n<20,1<m<n),在1~n中任取m個數,按字典序從小到大輸出所有這樣的排列。

題目原始碼:


#include <iostream> 
#include <cstring> 
using namespace std; c
onst int SIZE =25; 
bool used[SIZE]; 
int data[SIZE]; int n,m,i,j,k; bool flag; int main() { 
    cin>>n>>m; 
    memset(used,false,sizeof(used));  for(i=1;i<=m;i++)  { 
        data[i]=i; 
        used[i]=true;   } 
        flag=true;  
       while(flag)  { 
        for(i=1;i<=m-1;i++) 
        cout<<data[i]<<" ";   
        cout<<data[m]<<endl;   
        flag=/*空<1>*/;
        for(i=m;i>=1;i--)   { 
            /*空<2>*/;
            for(j=data[i]+1;j<=n;j++) 
            if(!used[j])     { 
                used[j]=true; 
                data[i]=/*空<3>*/
                flag=true;      
                break;      
            }    
            if(flag)    { 
                for(k=i+1;k<=m;k++) 
                for(j=1;j<=/*空<4>*/;j++)      
                if(!used[j])      
                { 
                    data[k]=j;      
                    used[j]=true;       
                    break;      
                } 
                /*空<5>*/       
            }   
        }  
    } 
    return 0;  
}

解題咯~

空<1>:因為flag是一個記錄變數,所以要一開始初始化為false。答案為false

空<2>:used代表著“使用過”,那麼這裡的data[i]沒有使用過,所以將data[i]設定為沒有使用過。即used[data[i]]=false

空<3>:如果j沒有使用過,就將j的值記入。即data[i]=j(實際填寫“j”)

空<4>:因為要從n個數裡選m個,所以這裡要列舉這n個數。即j<=n(實際填寫"n")

空<5>:因為做完了,所以退出迴圈。即break

接下來。

NOIP2013:二叉查詢樹

題意描述:二叉查詢樹具有如下性質:每個節點的值都大於其左子樹上所有節點的

值、小於其右子樹上所有節點的值。試判斷一棵樹是否為二叉查詢樹。 
輸入的第一行包含一個整數n,表示這棵樹有n個頂點,編號分別為1, 2, …, n,其中編號為1的為根結點。之後的第i行有三個數value, left_child, right_child,分別表示該節點關鍵字的值、左子節點的編號、右子節點的編號;如果不存在左子節點或右子節點,則用0代替。輸出1表示這棵樹是二叉查詢樹,輸出0則表示不是。

題目原始碼:

#include <iostream> using namespace std;  
const int SIZE = 100; 
const int INFINITE = 1000000;  
struct node {  
    int left_child, right_child, value; 
};  
node a[SIZE];  
int is_bst(int root, int lower_bound, int upper_bound) {  
        int cur; 
	if (root == 0)   
	return 1; 
	cur = a[root].value; 
	if ((cur > lower_bound) && (/*空<1>*/) && (is_bst(a[root].left_child, lower_bound, cur) == 1) && (is_bst(/*空<2>*/,/*空<3>*/,/*空4>*/) == 1))  
	   return 1; 
	return 0; 
}  
int main() {  int i, n;  cin>>n; 
	for (i = 1; i <= n; i++)   
	cin>>a[i].value>>a[i].left_child>>a[i].right_child; 
	cout<<is_bst(/*空<5>*/, -INFINITE, INFINITE)<<endl;
	return 0; 
}

空<1>:如果當前節點的值不存在(為0), 那麼不能進行操作。所以要保證cur大於0.所以填寫"cur"

空<2>:因為已經遍歷完左子樹,所以開始遍歷右子樹(設定為根)。填寫a[root].right_child

空<3>:這裡將最低的值設定成cur,因為要保證下一個數(在遍歷右子樹)大於這個數。

空<4>:這裡的最大值不需要改變(因為是遍歷右子樹),所以依舊是upper_bound

空<5>:這裡要從根節點開始。即1。

NOIP2014:最大子矩陣和

題意簡述:給出m行n列的整數矩陣,求最大的子矩陣和(子矩陣不能為空)。  
輸入第一行包含兩個整數m和n,即矩陣的行數和列數。之後m行,每行n個整數,描述整個矩陣。程式最終輸出最大的子矩陣和。

題目原始碼:

#include <iostream>   
using namespace std;   
const int SIZE = 100;  
int matrix[SIZE + 1][SIZE + 1];  
int rowsum[SIZE + 1][SIZE + 1]; //rowsum[i][j]記錄第i行前j個數的和  int m, n, i, j, first, last, area, ans;   
int main()    {  
    cin >> m >> n;  
    for(i = 1; i <= m; i++)        
    for(j = 1; j <= n; j++)          
    cin >> matrix[i][j];      
        ans = matrix/*空<1>*/ 
    for(i = 1; i <= m; i ++)        
        /*空<2>*/    
    for(i = 1; i <= m; i++)        
        for(j = 1; j <= n; j++)  
            rowsum[i][j] = /*空<3>*/      
    for(first = 1; first <= n; first++)        
    for(last = first; last <= n; last++){  
        /*空<4>*/ 
        for(i = 1; i <= m; i++)              {  
            area +=/*空<5>*/;          
             if(area > ans) ans = area;                
             if(area < 0)                  
             area = 0;              
             }          
    }  
    cout << ans << endl;      
    return 0;    
}
空<1>:ans的最大答案目前是子矩陣1行1列。所以設定為matrix[1][1]

空<2>:這裡是初始化操作,初始化rowsum。因為rowsum是用來統計前i行第j個數的和,所以初始化時就是rowsum[i][0]=0;(前i行第0個數)

空<3>:這裡是前i行第j個數的和。所以是前i-1行和加上當前數。當前數即matrix[i][j]。即rownum[i-1][j]+matrix[i][j]

空<4>:因為area沒有賦值。所以是初始化area,即area=0

空<5>:這裡要加上當前行的last和first-1兩個數。即rownum[i][last]+rownum[i][first-1]

NOIP2015:中位數

題意描述:給定n(n為奇數且小於1000)個整數,整數的範圍在0~m(0 < m < 231)
之間,請使用二分法求這n個整數的中位數。所謂中位數,是指將這n個數排序之後,排在正中間的數。

題目原始碼:

#include <iostream> using namespace std;  
const int MAXN = 1000;  
int n, i, lbound, rbound, mid, m, count; int x[MAXN];  
int main() {  
    cin >> n >> m; 
    for (i = 0; i < n; i++)         
    cin >> x[i];     
    lbound = 0;     
    rbound = m; 
    while (/*空<1>*/) { 
        mid = (lbound + rbound) / 2;             
        /*空<2>*/;     
        for (i = 0; i < n; i++)             
        if (/*空<3>*/)                    
            /*空<4>*/;     
        if (count > n / 2)             
        lbound = mid + 1;         
        else 
        /*空<5*/       
    } 
    cout << rbound << endl;     
    return 0; }

空<1>:因為是二分法,所以必須要保證左小於右。所以就是lbound<=rbound

空<2>:這裡要將計數器count置為0(根據下面的count>n/2得知).即count=0

空<3>:這裡其實要判斷有多少數比mid大(根據下面的if(count>n/2)得知),那麼這裡就是填寫mid<x[i]

空<4>:計數。即count++;

空<5>:屬於二分的最後操作,將右賦值為mid-1。即rbould=mid-1。

好吧。。。。終於做完了。。。