Try   HackMD

I/O Stream 的控制

tags: Competitive Programming Note

本文已停止更新,新版請至 WiwiHo 的競程筆記

運算子重載

重載 <<>> 就能讓原本不能直接輸入輸出的東西可以直接 cin/cout 了,例如:

ostream& operator<<(ostream& o, pair<int, int> p){ return o << p.first << " " << p.second; } istream& operator>>(istream& i, pair<int, int> p){ return i >> p.first >> p.second; }

輸入格式

EOF

EOF 是 End of File 的縮寫。有些題目會在一個檔案放多筆測資,比較現代的題目會先輸入一個數字告訴你接下來有幾筆,但比較古老的題目或心理比較古老的人出的題目就會跟你說「以 EOF 結束」,所以你必須一直讀到檔案結束為止,做法很簡單:

while(cin >> /*...*/){ //... }

cin >> ... 除了把東西輸入到變數裡,也會回傳 cin 本身(這就是為什麼可以像 cin >> a >> b >> c >> ... 串一大串),而 cin 可以轉型成 bool,如果遇到 EOF(或其他原因導致輸入失敗)了它就是 false,否則它就是 true。

EOF 這東西不是一個字元,所以你可能會想,terminal 不是 file,不就沒有 EOF 了嗎?在 Windows 可以用 ctrl+z 製造出 EOF,其他系統可以用 ctrl+d。

輸入一整行

用像是 cin >> ... 的方式輸入的話,會先忽略緩衝區前面的所有空白字元(空格、換行等等),然後讀取非空白字元,直到遇到空白字元再停下來。

但是,當遇到這種情況,就會需要一次輸入一整行:你需要知道哪些東西在同一行,而且又不確定一行有幾個,方法是這樣:

string s; getline(in, s); //in 是輸入流

getline 也可以放進 while 裡判 EOF。

getline 跟 >> 混用的話會發生一個問題,假設有兩行輸入,第一行有一個數字,下一行有未知數量的以空格隔開的數字,首先它們會全部進入緩衝區,如果你先用 >> 輸入了一個數字,那麼在緩衝區內的這個數字會被移除,但它後面的換行不會,所以你這個時候用 getline,就得到一個空字串,解決方法也很簡單,就是 getline 一次把空白行清掉後,再 getline 一次。

忽略輸入

直接看題目:ZeroJudge c268,這題表面上是要你找三個數字當三角形的邊長,可以

O(nlogn) 做完,可是第一
n
超大,把數字排序好後就 TLE 了,就算不排序,雖然輸入的複雜度是
O(n)
,但輸入常數很大,輸入這麼多東西也會 TLE 的。

可以發現到若輸入的數字都不能構成三角形,那麼在數字都盡量小的情況下,把它們排序後會變成費氏數列,而輸字的最大值又是

109,費氏數列還沒到第 50 項就會超過了,所以可以得出
n50
的時候保證有解。

現在排序的問題解決了,那輸入的問題呢?它是一個檔案多筆測資,所以也不能讀到

n50 就直接 return,解決方法是:

cin.ignore(100000000, '\n');

這麼做可以忽略掉到一部分的輸入,第一個參數是忽略的字元上限,打一個很大的數字就行了,第二個是停下來的字元,到下一個這個字元之前和它本身的字元都會被忽略掉。

這也是 getline 解決空行的解法之一。

輸出格式

小數位數

需要輸出浮點數的題目,要嘛就是要求跟答案的差距要在某個範圍內,不然就是要求你四捨五入到第幾位,所以我們要能夠控制輸出的小數點後位數。方法是這樣:

cout << fixed << setprecision(10) << ...;

沒加 fixed 的話,會變成是指定總共的位數,如果小數點前的位數超過就會變科學記號,而加上 fixed 就是指定小數點後的位數了,然後它也會自己四捨五入,非常方便。還有它只作用在浮點數,像是 int 不會跑出 .0000,字元也還是以字元的形式輸出。

數字寬度

數字寬度和數字位數不一樣,輸字寬度是指輸出的總字元數不到寬度的時候,就會在它之前補上數個某固定字元。指定寬度是 setw(n),補上的字元是 setfill(c),預設空格,用法就一樣是 cout << setw(n) << setfill(c) << ...。要特別注意的是,setw 只作用在下一個輸出的數字(可以是浮點數或整數)上,而 setfill 不會失效。