CCU飯店共有N間房間( 0<N<300 ),且提供入住房客指定預入住房號的服務, 如果房客指定房間已有人入住,則提供下一間房間給房客, 亦即原指定房號加一。
但是由於房數眾多,辦理入住櫃檯經常會忘記哪個房間已有人入住。 因此,CCU飯店管理人員決定要請資訊部門幫忙研發一套簡單的軟體, 能夠記錄目前有哪些房間,已有人進住。
Input 第一行數字為房間數,N值。 第二行數字為房客依序指定的房號,以空格分隔,以-1表示輸入結束。 Output 第一行數字為房號001、002、 003、…中間以空格隔開 第二行數字為房間入住情形,若房間已有人入住,則顯示1;若暫無人入住,則顯示0,最後面的0/1後接換行符號,無空格。 第三行數字為房客入住房號順序,以空格格開;最後面的數字後接換行符號,無空格。
Sample Input
10
2 3 9 1 9 9 -1
Sample Output
001 002 003 004 005 006 007 008 009 010
1 1 1 1 0 0 0 0 1 1
2 3 9 1 10 4
根據題意,我們先處理輸入的部分,得到以下架構:
#include <stdio.h>
#include <stdlib.h>
int main() {
int N;
scanf("%d", &N);
int query;
while (scanf("%d", &query)) {
if (query == -1) break;
}
return 0;
}
###房間資訊
我們首先需要一個陣列來儲存各個房間有沒有住人的資訊,我個人採取的儲存方法是,若房間i
有人住,則 data[i-1] = 1
,要不然 data[i-1] = 0
,也就是說
房間 1 的資訊,存在 data 的第 0 項。 房間 2 的資訊,存在 data 的第 1 項。 房間 3 的資訊,存在 data 的第 2 項。 ...
這樣寫,data
陣列的宣告只需要宣告成長度為 N 的陣列。
####第 2 種
當然你要寫成
int data[N+1];
然後捨棄 data[0]
不用。之後
房間 1 的資訊,存在 data 的第 1 項。
房間 2 的資訊,存在 data 的第 2 項。
房間 3 的資訊,存在 data 的第 3 項。
...
這樣當然也可以。(只是之後你要稍為修改我教學中的程式碼)
另外我們還需要一個陣列來儲存每個客人最後住到哪間房間去了。因為我們不知道總共有幾個客人,但我們知道客人一定不會比 N 還多(程設實習時,我有問過助教,助教說保證不會出現客人人數比房間數多的情況)。
我們想依序將「客人最終住房資訊」存到陣列裡,所以還需要一個變數來記錄「正在詢問的客人,是目前為止第幾個客人(從 0 開始數,這樣可以直接對應到陣列的第幾項)」。
int record[N];
int idx = 0;
當客人詢問 query
號房可不可以住時,有兩種情況:可以住、不可以住,若不可以住的話要尋找可以住的房間。
尋找的規則為:
query+1
可不可以住,若不可以,則
query+2
可不可以住,若不可以,則
query+3
可不可以住,若不可以,則
...
若我們尋找到 第 N 號房,且第 N 號房已經有人住,我們得從第 1 號房開始找起。一直找,直到找到空房為止。因為客人數一定不會大於房間數,所以不用處理找不到空房的情況。
以上的邏輯對應的程式碼可以寫成這樣:
int test = query;
while (data[test] != 0) {
/*這裡假設你房間資訊的儲存方式是上述的第二種。*/
test = test + 1;
if (test == N+1)
test = 1;
}
若你跟我一樣,房間資訊的儲存方式是第一種,則程式是寫成:
int test = query-1; /*房號 query,對應陣列的第 query-1 項 */
while (data[test] != 0) {
test = test + 1;
if (test == N)
test = 0;
}
或者也可以這樣寫,這種寫法只適用第 1 種儲存方式。
int test = query-1;
while (data[test] != 0) {
test = (test+1) % N;
}
不管哪種寫法,在迴圈執行完後,test
都會停留在空房上(也是說,data[test]
會是 0)。
也許你會好奇,如果一開始,客人詢問的房號就是空房(data[test] = 0
),不是應該寫一個 if
來判斷,我們不需要去尋找空房啊。
你可以仔細看我的 while
迴圈的條件,我是用 while
而不是 do...while
,如果一開始客人詢問的房號就是空房,那麼這個 while
迴圈根本連進入都不會進入。
到目前為止,整個程式碼可以寫成(房間資訊採用第 1 種做法):
#include <stdio.h>
#include <stdlib.h>
int main() {
int N;
scanf("%d", &N);
int data[N];
memset(data, 0, sizeof(data)); /*初使化陣列*/
int record[N];
memset(record, 0, sizeof(record));
int idx = 0;
int query;
while (scanf("%d", &query)) {
if (query == -1) break;
int test = query-1;
while (data[test] != 0)
test = (test+1) % N;
/*既然找到了空房,就讓客人住進去*/
data[test] = 1;
/*這個客人最終住到了陣列的第 test 項,也就是房號 test+1*/
record[idx] = test+1;
idx++;
}
return 0;
}
###第一行 補零、寬度為 3 這沒什麼好說的,請自行翻閱文件。
這種卡你空格的題目在 uva 上不少見,請一定要將這種方法學起來。(有些題目還有會要你加入空白行之類很麻煩的東西)
因為輸出 不是
001 002 003
而是
001 002 003
你可以用滑鼠分別反白上面兩行,會發現第一行最後是有一個空白(空格)的,而第二行就沒有。
所以這樣寫絕對會錯:
int i;
for (i=0; i<N; i++)
printf("%03d ", i+1);
printf("\n");
要處理這個有很多方法,我的做法是:
除了第一項以外,在要輸出的數字前面輸出一格空格。
其它方法,包含有:
除了最後一項,在要輸出的數字後面輸出空格。
雖然說上述的後者比較直覺,但如果你以後持續在寫 uva ,你會發現我們有時很難知道 i
的最後一項是什麼,因為迴圈在中途就 break 了。而 i
的第一項我們幾乎都是知道的。所以我大部分都是用前者的寫法。
int i;
for (i=0; i<N; i++) {
if (i != 0)
printf(" ");
printf("%03d", i+1);
}
printf("\n"); /*記得換行*/
###第二行 同樣的,輸出的最後 沒有 空格。 觀察一下,應該就會發現一些規則,我找到的是:
除了第一項的數字前面是一個空格,其它項的數字前面都是三個空格。
所以程式碼就很好寫了:
for (i=0; i<N; i++) {
if (i == 0)
printf(" ");
else
printf(" ");
printf("%d", data[i]);
}
printf("\n"); /*記得換行*/
###第三行
這行也很簡單,就把我們存在 record
裡的資訊輸出而已,當然還是要注意空格。比較有問題的是我們不知道我們在 record
裡到底存了幾個客人的資訊。
還記得我們之前將資訊存入 record
之後,我們都會將 idx++
,所以在這一次迴圈結束時,idx
的值是已經指向下一個該存入資訊的位置了。當整個迴圈結束後,idx
的值也相當於是客人的人數。
例如,假設 while
迴圈執行 6 次,並在第 6 次時,一進入迴圈馬上就跳出。在 while
第 5 次迴圈結束時,idx 變成 5。第 6 次時,沒有更新到 idx 的值,所以在整個迴圈結束時,idx 就是 5
,也就是客人的人數。
所以第三行我們直接這樣寫就可以了:
for(i=0; i<idx; i++) {
if (i != 0)
printf(" ");
printf("%d", record[i]);
}
printf("\n"); /*記得換行*/
第一次使用 markdown 寫這麼長的文章,比起之前那樣寫,現在這樣寫你們感覺有比較好嗎?
Written with StackEdit.
By Yu-Cheng Huang Nov. 4, '14