解密計算機與資訊學競賽
阿新 • • 發佈:2018-12-17
身邊有很多人會問我這個學習資訊競賽還不到一年的OIer:
“你們計算機學什麼啊?”
“你們怎麼判斷得分啊?”
“你們是不是天天在機房玩遊戲啊?”
......
受到之前看到過的一篇文章的啟發: 《有趣又有用的資訊學競賽》。
而且,幾乎所有人以後都會學計算機(據我所知現在已經有其他競賽的大佬在學了)。
我決定修改一下原文,給大家的計算機入個門,把資訊學競賽介紹一下。
計算機到底在學什麼亂七八糟的東西?
簡而言之:程式設計。
程式設計主要分為三個部分:
1.語言。
NOIP的程式語言目前僅支援三種,Pascal,C,以及C++,不過從2022年開始,將取消對C以及Pascal的支援,所以我們現在用的都是C++。那些看《啊哈C》和《啊哈演算法》的,我只能呵呵了。
本人是拿這本書入門的:
一般來說,人生的第一份程式碼,要麼是:
#include <iostream>
using namespace std;
int main() {
cout << "Hello world!" << endl;
return 0;
}
要麼是:
#include <iostream> using namespace std; int main() { int a, b; cin >> a >> b; cout << a + b << endl; return 0; }
這兩份程式碼,初學者不用管最基本的程式框架:
#include <iostream>
using namespace std;
int main() {
return 0;
}
打熟就完事了。 (當年打了十幾遍才熟的蒟蒻路過......)
第一份程式碼中:
cout << "Hello world!" << endl;
含義是:輸出(cout)“Hello world!”,換行(endl)。
第二份程式碼中:
int a, b; cin >> a >> b; cout << a + b << endl;
含義是:定義(int)a,b;輸入(cin)a,b;輸出(cout)a+b,換行(endl)。
當然,我的解釋並不準確,但對於入門者來說,足夠了。
簡單吧?
下面給大家一份可以讓滑鼠亂動的程式碼:
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;
int main() {
int a, b, x, y, i;
while (1) {
i++;
srand(time(0));
a = rand() % 1000;
b = a * i % 2000;
x = a * b % 2001;
y = (a * x + b) % 3001;
SetCursorPos(x, y);
}
return 0;
}
現在的問題是,有了程式碼,怎麼讓計算機編譯呢?
大家可以下載一個軟體Dev-C++。軟體安裝完成後,點選檔案->新建->原始碼,或者直接按住Ctrl+N新建原始碼,就可以寫程式碼了。
寫完後,點選執行->編譯執行,或者直接按F11,如果是第一次編譯,需先儲存。(編譯時最好把360軟體關閉,不然會被當成病毒......)
就像這樣:
提醒一下,如果編譯讓滑鼠亂動的程式碼,編譯後就用不了滑鼠了!!!其實可以按Ctrl+C退出來的。
其實基本的C++語言不難學, 而像“指標”這種高階技能,基本用不到, 所以最多一個月就足夠掌握了。
2.演算法
演算法其實就是解決問題的方法,放在計算機裡就是用程式解決問題的方法。
舉個並不那麼確切的例子:
1+2+3+4+......+(n-2)+(n-1)+n=?
或許大家小學就知道怎麼做這道題。
方法一:一個一個慢慢加,答案肯定能算出來......
這種方法大約要計算n次,我們稱時間複雜度為O(n)。
方法二:利用公式(n+1)×n÷2。
這種方法只用計算3次,我們在算時間複雜度的時候一般忽略係數和常數,因此這種方法的時間複雜度為O(1)。
對於同一個問題,你可能有很多種解決方案,當然不同的解決方案優劣不同,比如例子中的方法一,你可能算著算著會出錯,或者是心態爆炸。一般人肯定更願意採用更快,更簡單的方法來解決問題,對於此問題中也就是方法二。可見演算法是有優劣性的。
計算機運算速度很快,但肯定有上限,我們通常按1億次每秒來計算,或許你會說,反正都那麼快,那還管什麼演算法優劣性,這裡我們又要舉個例子了。
就把n個不同身高的人從低到高排序好了。
第一次你需要在n個人裡面找最高的;
第二次你需要在(n-1)個人裡面找第二高的;
第三次你需要在(n-2)個人裡面找第三高的;
以此類推......
我們來算算當n=10000000(1000萬)時,一共需要比較幾次的身高。
第一次也就那麼10000000次;
第二次也就那麼9999999次;
第三次也就那麼9999998次;
就算它最後一次不用比較好了......
其實總共也就那麼(10000000+2)×9999999÷2=50000004999999次(約五十萬億次)......
那麼計算機需要做多久呢?
每秒一億次,一分鐘60秒,一小時60分鐘,一天24小時......
也就那麼......
6天!!!
我估計下載遊戲可能會有人等6天,這種無聊的東西,沒有人會願意等個6天......
顯然這種演算法的時間複雜度為O(n^2)。
有沒有更快一些的演算法呢?
有的,比如快速排序,它可以用O(nlogn)的時間複雜度完成排序。同樣是n=1000萬的資料下,它只需要2秒的時間。計算機並沒有變快,都是一樣的速度,所應用的演算法不同,解決問題的效率完全不一樣。
3.資料結構
資料結構是計算機儲存、組織資料的方式。資料結構是指相互之間存在一種或多種特定關係的資料元素的集合。通常情況下,精心選擇的資料結構可以帶來更高的執行或者儲存效率。資料結構往往同高效的檢索演算法和索引技術有關。
上面那段話當然不會是我寫的,摘自百度百科。
資料結構比較複雜,我就不深入了。
一份程式的得分怎麼算?
拿剛才那道a+b的題為例。
我們會拿幾組測試資料來驗證這個程式的正確性。
比如,如果拿10組測試資料,如下:
6 7
66 77
666 777
6666 7777
66666 77777
666666 777777
6666666 7777777
66666666 77777777
666666666 777777777
6666666666 7777777777
那麼,剛才那份程式:
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a + b << endl;
return 0;
}
對每組測試資料,會輸出:
13
143
1443
14443
144443
1444443
14444443
144444443
1444444443
-1369705872
好,滿分100,10個點,對了9個,得分90。
很顯然,最後一個點輸出錯了,為什麼?
剛才我說,int是指定義,其實不準確,int只能定義-2147483648~2147483647(2的31次方減1)之間的整數。6666666666和7777777777都超了,因此輸出會出問題(至於為什麼會輸出-1369705872,這涉及到資料的儲存方式,就不解釋了)
那怎麼辦呢?其實很簡單,把int改成long long即可,long long的範圍為-9223372036854775808~9223372036854775807(2的63次方減1),即:
#include <iostream>
using namespace std;
int main() {
long long a, b;
cin >> a >> b;
cout << a + b << endl;
return 0;
}
這樣,最後一個點的輸出為:
14444444443
100分到手!
一般來說,做一道題時,我們並不知道測試資料是什麼,但是題目會給出測試點的取值範圍,甚至是每個測試點的取值範圍。
我們來看一道今年NOIP的題目: