Test Message

c700: 壞掉的隊列 (queue)

內容

「測資中有若干行輸入,請你實作 queue 的幾種操作:

push X(0≤X<232): 將 X 加入隊列

pop: 輸出隊列最前方的數字並刪除,你可以假設此時隊列不是空的」

小 W 本來想隨便寫寫交差了事,卻發現 STL 的 queue 壞了!

再看看題目,原來底下附註一行小字:請用兩個 stack 完成這題。

於是小 W 希望你能用以下代號寫一張紙條告訴他該怎麼做。

1: 讀入 push X 並將 X 放到堆疊一

2: 讀入 push X 並將 X 放到堆疊二

3: 讀入 pop ,將堆疊一頂端的元素輸出並刪除

4: 讀入 pop ,將堆疊二頂端的元素輸出並刪除

5: 將堆疊一頂端的元素取出並放至堆疊二

6: 將堆疊二頂端的元素取出並放至堆疊一

如果取出元素時堆疊為空或者讀入 push/pop 的順序與輸入測資不符,你會害小 W 拿到一個 WA。


輸入

見題目和範例。

push 111
push 222
pop
pop

輸出

輸出一行你要傳給小 W 的內容。

範例輸出一:
1234

範例輸出二:
12544

範例輸出三:
1143

範例輸出四:
1313

範例輸出五:
1133

提示

輸入至多 100000 行。

以範例輸入而言,範例輸出一、二會拿到 AC。

範例輸出三會拿到 WA,因為操作 4 時堆疊二是空的。

範例輸出四也會拿到 WA,因為輸入順序是 push->push->pop->pop,但是 1313 的操作分別為 push->pop->push->pop。

範例輸出五的操作過程完全合法,但依據先進先出的原則,111 應該比 222 早離開 queue,若以 1133 的方式操作,222 將比 111 早輸出,所以會拿到 WA。


解題思路

用兩個 stack 模擬 queue,重點觀念是 stack 的輸出順序是反向的,但如果先把 stack 存到另一個 stack 再輸出,根據負負的正定理,新 stack 的輸出順序就會變成正常的,如以下範例

  1. 輸入 = 1 2 3 4 5
  2. 依序 push 進 stack1
  3. stack1 = 1 2 3 4 5
  4. 再將 stack1 依序 pop 出並 push 進 stack2
  5. stack2 = 5 4 3 2 1
  6. 最後將 stack2 依序 pop 輸出
  7. 輸出 = 1 2 3 4 5

也就是說實作時

  • 指令為 push 時,將輸入的東西都存進 stack1,
  • 指令為 pop 時,看 stack2 還有沒有剩餘的元素,有就 pop 出該元素,否則將 stack1 依序 pop 到 stack2
  • 由於本題並不需要輸出任何有關輸入的的元素,所以可以無視它用兩個 int 模擬 stack 的長度來做就行了。

完整程式碼

AC (46ms, 704KB)
#include <stdio.h>

int s1Len, s2Len;
char cmd[20], output[200000], * oTop = output;

int main()
{
while (gets(cmd) != NULL)
{
if (cmd[1] == 'u')
{
s1Len++;
*oTop++ = '1';
}
else if (s2Len)
{
*oTop++ = '4';
s2Len--;
}
else
{
s2Len = s1Len - 1;
while (--s1Len)
* oTop++ = '5';
*oTop++ = '5', * oTop++ = '4';
}
}
puts(output);
return 0;
}

c669: missing and duplicate

內容

給您 T 個打亂的等差數列,請你找出數列中少了的數字,以及重複的數字。


輸入

測資第一列有個數字 T , T <= 200

以下有 T 個打亂的數列,請找出遺失的數 m , 重複的數 d

數列長度 >= 5

min < m < max

min <= d <= max

2
7 9 1 3 7
5000 8000 9000 8000 7000

輸出

輸出 m 和 d

5 7
6000 8000


解題思路

將輸入字串轉成整數排序,根據題意,m 不會為最大或最小項,所以等差即是 (min + max) / (len - 1)

有等差之後從第二個元素開始遍歷陣列,如果發現不符合等差規則的話再判斷是重複出現的或是缺少數字即可。


完整程式碼

AC (3ms, 112KB)

#include <stdio.h>
#include <stdlib.h>
#define MAX 10000

int kase, list[1000], tmp, nLen, gap, same, miss;
char input[MAX], ans;

int cmp(const int* lhs, const int* rhs)
{
return *lhs - *rhs;
}

void setDigitList()
{
tmp = nLen = 0;
for (int i = 0; input[i]; i++)
{
if (input[i] == ' ')
list[nLen++] = tmp, tmp = 0;
else
tmp = (tmp << 3) + (tmp << 1) + (input[i] - '0');
}
list[nLen++] = tmp;
}

int main()
{
scanf(" %d", &kase);
getchar();
while (kase--)
{
gets(input);
setDigitList();
qsort(list, nLen, sizeof(int), cmp);
gap = (list[nLen - 1] - list[0]) / (nLen - 1), ans = 0;
for (int i = 1; i < nLen; i++)
{
if (list[i] != list[i - 1] + gap)
{
if (list[i] == list[i - 1])
same = list[i], ans++;
else
miss = (list[i] + list[i - 1]) >> 1, ans++;
if (ans == 2)
break;
}
}
printf("%d %d\n", miss, same);
}
return 0;
}

c665: 進制轉換

內容

16 進位是以 16 為底的進位制,用 0 ~ 9, A ~ F 來表示。
36 進位是以 36 為底的進位制,用 0 ~ 9, A ~ Z 來表示。
本題要請您設計一個可以將數字在 2 ~ 36 進制間轉換的程式。

每行測資將會有 n b1 b2 三個文數字
n 是以 b1 為底的文數字,請把它轉換為 b2 進制後輸出。


輸入

101 2 4
CQF 27 3
10010011100110100 2 14

輸出

11
110222120
1D780


解題思路

先將輸入轉成 10 進制,再將 10 進制轉成目標進制的輸出。


完整程式碼

AC (2ms, 100KB)
#include <stdio.h>

const char A = 'A' - 10;
int b1, b2, rem, sum;
char input[2000], * output;

int main()
{
while (scanf("%s %d %d", input, &b1, &b2) == 3)
{
output = input + 999;
sum = 0;
for (int i = 0; input[i]; i++)
{
if (input[i] <= '9')
sum = sum * b1 + (input[i] - '0');
else
sum = sum * b1 + (input[i] - A);
}
while (sum)
{
rem = sum % b2;
*--output = rem <= 9 ? rem + '0' : rem + A;
sum /= b2;
}
puts(output);
}
return 0;
}

c659: 連接詞

內容

請用指定的連接詞連接所有的單字。

輸入

輸入只有一行,第一個字為連接詞,第二個以後為單字。連接詞與單字之間均以空白隔開。連接詞與單字包含字母及 / 或符號,但不含空白。

and apple banana cantaloupe

輸出

輸出一行,含有所有的單字,單字與單字間均有一個連接詞。

apple and banana and cantaloupe


解題思路

先讀取第一個字串當作連接詞,之後將每個字串中間的空白取代為連接詞即可。


完整程式碼

AC (2ms, 108KB)
#include <stdio.h>
#define SIZE 10000

char input[SIZE], output[SIZE], conj[100], cLen, i, oLen;

int main()
{
gets(input);
for (i = 0; input[i] != ' '; i++)
conj[cLen++] = input[i];
i++;
for (; input[i]; i++)
{
output[oLen++] = input[i];
if (input[i] == ' ')
{
for (int j = 0; j < cLen; j++)
output[oLen++] = conj[j];
output[oLen++] = ' ';
}
}
puts(output);
return 0;
}

c658: 小新的提款卡密碼

內容

小新習慣將提款卡的密碼寫在卡片上,因為怕被盜領,小新做了如下的動作:

  1. 密碼數字是一個完全平方數。
  2. 將此數字重新排列後才寫在卡片上。
    由於提款機輸入密碼時有次數的限制,請您先將可能的密碼列出。

輸入

數字每行是一個 4 ~ 10 位數的數字。
數字內不含 0

1269
3133

輸出

請將輸入的數字重新排列後,依序輸出符合條件的密碼。
如果找不到密碼請輸出 0

1296 2916 9216
0


解題思路

因為題目表明沒有 0 且位數是再 4~10 位數,所以在建表的時候,只需要建 36~99999 的平方項,且如果該項為 0 可直接跳過不紀錄。
而這題原本想建好表之後用 DFS 配合 BinarySearch 查表來解,無奈 10! 的可能性過多,最後 3 筆測資 TLE,所以改用 HashTable 來做。

根據題意只要是由 1,2,6,9 四個字組合而成的數,都要輸出 { 1296、2916、9216 } 三個,所以將它們視為一組答案,而既然他們是一組答案又要使用 HashTable 來做,1296、2916、9216 就應該共用一個 HashKey,所以在製成 HashKey 時,用基數排序紀錄每個數有幾個,再將它們打成 HashKey,如此一來不管原本是多少,只要構成的字元一樣,得到的 HashKey 就會是一樣的。

將表建好之後只要每次將輸入的字串打成 HashKey 查表,若有查到相同的答案則輸出,若否則代表這組數字組合無法組合成完全平方數,輸出 0。


完整程式碼

AC (33ms, 7.3MB)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Hash
{
unsigned int Key, vLen;
char Values[40][11];
struct Hash* Next;
}Hash;

Hash* pList[32768];
Hash** now;
long long tmp;
unsigned int key, pLen;
char value[11];

inline unsigned int APHash(char src[])
{
unsigned int hash = 0;
char str[10] = { 0 };
for (int i = 0; src[i]; i++)
str[src[i] - '0']++;
for (int i = 9; i; i--)
{
if (i & 1)
hash ^= (~((hash << 11) ^ (str[i]) ^ (hash >> 5)));
else
hash ^= ((hash << 7) ^ (str[i]) ^ (hash >> 3));
}
return (hash & 0x7FFFFFFF);
}

inline void addHash(long long square)
{
sprintf(value, "%lld", square);
key = APHash(value);
now = &pList[key & 32767];
while (*now)
{
if ((*now)->Key == key)
{
strcpy((*now)->Values[(*now)->vLen++], value);
return;
}
now = &(*now)->Next;
}
(*now) = malloc(sizeof(Hash));
(*now)->Key = key;
(*now)->vLen = 1;
strcpy((*now)->Values[0], value);
(*now)->Next = NULL;
}

inline void getAns(char src[])
{
key = APHash(src);
now = &pList[key & 32767];
while (*now)
{
if ((*now)->Key == key)
{
for (int i = 0; i < (*now)->vLen; i++)
printf("%s ", (*now)->Values[i]);
putchar('\n');
return;
}
now = &(*now)->Next;
}
puts("0");
}

int main()
{
for (long long i = 34; i < 100000; i++)
{
tmp = i * i;
while (tmp % 10)
tmp /= 10;
if (!tmp)
addHash(i * i);
}
while (scanf(" %s", value) == 1)
{
getAns(value);
}
return 0;
}

c657: 最長連續字母

內容

測資有若干行字串。
每個字串是由小寫字母組成。
請找出字串中最長連續的字母,並輸出其長度。
如果有長度相同的連續字母,請輸出最先出現的。


輸入

abbcc
cciiiiiiiixxxxxxxxxxxxguuuuuuufugpccccccc

輸出

b 2
x 12


解題思路

簡單的迴圈與條件判斷。


完整程式碼

AC (2ms, 116KB)
#include <stdio.h>

char input[10000], curr, bestC;
int count, best;

int main()
{
while (scanf(" %s", input) == 1)
{
count = best = 0;
for (int i = 0; input[i]; i++)
{
if (input[i] == curr)
count++;
else
{
if (best < count)
best = count, bestC = curr;
count = 1, curr = input[i];
}
}
if (best < count)
best = count, bestC = curr;
printf("%c %d\n", bestC, best);
}
return 0;
}

c638: 天干地支

內容

天干,是中國古代的一種文字計序符號,共 10 個字: 甲、乙、丙、丁、戊、己、庚、辛、壬、癸,循環使用。( wiki )
地支是指木星軌道被分成的十二個部分,記為子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥。木星的公轉週期大約為十二年,所以中國古代用木星來紀年,故而稱為「歲星」。後來又將這十二個部分命名,這就是「地支」。( wiki )
.....
西元 1906 年(丙午年) 於德國柏林召開的國際無線電通信大會中,決定以「SOS」為國際求救信號(1908 年 7 月 1 日起正式使用)( wiki )
.....
給您若干個西元年,請算出其天干地支。 1800 <= y <= 2018


輸入

1906
1895

輸出

丙午
乙未


解題思路

先找到小於 1800 年的最後一個甲子年: 1744 年

我們知道一甲子為 60 年,所以將 n 設為 (輸入值 - 1744) % 60,
然後我們又知道天干 10 年一輪、地支 12 年一輪,將 n 分別 %10、%12 即是該年的天干地支。


完整程式碼

AC (2ms, 104KB)
#include <stdio.h>

char sky[][4] = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
char grd[][4] = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };

int main()
{
int n;
while (scanf(" %d", &n) == 1)
{
n = (n - 1744) % 60;
printf("%s%s\n", sky[n % 10], grd[n % 12]);
}
return 0;
}

c636: 十二生肖

內容

民國元年 ( 1912 ) 是鼠年。
給定若干個民國年份 -100 ~ 107
請輸出該年生肖為何。


輸入

1
107

輸出



解題思路

本題重點 : 因為元年是 1,所以民國沒有 0 年…

因為有負值,所以當 n 為負數時加上一個大於負數最大值的值讓他大於 0。


完整程式碼

AC (2ms, 84KB)
#include <stdio.h>

int n;
char Zodiac[][4] = { "豬", "鼠", "牛", "虎", "兔", "龍", "蛇", "馬", "羊", "猴", "雞", "狗" };

int main()
{
while (scanf(" %d", &n) == 1)
{
puts(Zodiac[(n < 0 ? n + 121 : n) % 12]);
}
return 0;
}

c562: Puyu 愛數論

內容

對於精通數論題目的 Puyu,號稱沒有數論題目是他解不出來的。於是擅長出奇怪水題的 CTF 從天上降臨到了人間準備跟他來一較高下。

CTF 給 Puyu 幾組數字,分別代表函數帶進去的值與函數結果,請他找出其中的規律,並回答 CTF 問的一些數字會產生的結果。

函數如下:

F ( 110 ) = 1 , F ( 163 ) = 1 , F ( 223 ) = 0 ,

F ( 119 ) = 1 , F ( 278 ) = 2 , F ( 821 ) = 2 。

現在請你觀察函數規律,並寫個程式來幫助 Puyu 來完成挑戰!


輸入

每筆測資包含多筆詢問。

每行有一個數字 N ,代表 CTF 問 Puyu 的數字

( 0 <= N <= 2147483649 )

223
821
734

輸出

每行請輸出一個數  F ( N ) 。

F ( N ) 必在 int 範圍。

0
2
0


解題思路

找圈圈

  • 0 = 1
  • 6 = 1
  • 8 = 2
  • 9 = 1

輸出輸入字串共有幾個圈即可。


完整程式碼

AC (5ms, 96KB)
#include <stdio.h>

int ans;
char list[11];

int main()
{
while (scanf(" %s", &list) == 1)
{
ans = 0;
for (int i = 0; list[i]; i++)
{
if (list[i] == '0') ans++;
else if (list[i] == '6') ans++;
else if (list[i] == '8') ans += 2;
else if (list[i] == '9') ans++;
}
printf("%d\n", ans);
}
return 0;
}

c561: Bert 愛搗蛋

內容

Bert 是個叛逆的學生,每次派任給他的任務都會做的亂七八糟,舉例來說,要他幫社團添購一台新電腦,卻買來一台疑似二手機的 AXXS 電腦,還付了兩萬多塊。

現在 CTF 想要找社團中最會寫程式的人擔任下一屆的教學,所以將每個人的能力值丟給 Bert ,但他也知道 Bert 非常喜歡搗蛋,會將所有能力值倒轉後再找一個最大值給 CTF ,CTF 非常聰明的先將所以值倒轉後再給 Bert ! 這樣就能得到正確的答案。

現在給你 CTF 將要給 Bert 的所有人的能力值,請你找出社團下屆教學的能力值。

數字倒轉的定義如下:

123 -> 321

147 -> 741

( 保證不會有前導 0 的問題 )


輸入

第一行共兩個數字 n ,代表電研社共有 n 個人。 (1 <= n <= 100000)

接下來一行共 n 個數字 a [ i ] ( 1 <= a [ i ] <= 10000 ) ,代表 CTF 給 Bert 第 i 個人的能力值。

5
23 73 92 32 21

輸出

輸出一個數,代表社團中最會寫程式的人的真正能力值。

37


解題思路

本題重點在數值的反轉,反轉步驟如下:

  1. 設 a = 轉換前的值, b = 轉換後的值 (預設值為 0)
  2. 將 b * 10
  3. 取 a % 10,讓 b 加上得到的值
  4. 把 a / 10,讓個位數消失,十位變個位,百位變十位…
  5. 判斷 a 是否為 0 ,是則跳出,否則回到 1

有辦法得到反轉值後,剩下的就是基本的條件判斷了。


完整程式碼

AC (12ms, 124KB)
#include <stdio.h>

int n, tmp, convert, best;

int main()
{
scanf(" %d", &n);
while (n--)
{
convert = 0;
scanf(" %d", &tmp);
while (tmp)
{
convert = convert * 10 + (tmp % 10);
tmp /= 10;
}
if (best < convert)
best = convert;
}
printf("%d\n", best);
return 0;
}
1121314151627