PaizaオンラインハッカソンVol.6に参加してみた(霧島京子ミッション, C言語)

今回はPaizaオンラインハッカソンVol.6に参加してみた(霧島京子ミッション, C++)をC言語で記述した場合のコードを上げてみた。一度C++で成功しているので、今回はそれほど苦労することはなかった。ただ、C++で使える強力なライブラリー等が使えないのが若干面倒だったが。

回答例

[code lang=”c”]#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1000

/// 改行コードを除去する
/// @param str 改行コードを除去する対象の文字列
static void removeReturnCode(char *str) {
if (strchr(str, ‘\n’)) {
// str[strlen(str) – 1] = ‘\n’; // これは間違い(cf: コメント欄)
str[strlen(str) – 1] = ‘\0’;
}
}

/// 文字列を元に整数型の配列を生成する
/// @param str 文字列
/// @param dst 対象の配列へのポインタ
/// @param size 配列のサイズ
static void separateStringToArray(const char *str, int *arr, size_t size) {
char *tempStr = (char *)calloc(sizeof(char), BUFFER_SIZE);
const char *sepStr = " ";
strncpy(tempStr, str, strlen(str) + 1);
for (int count = 0; count < size; count++) {
const char *word = strtok(count == 0 ? tempStr : NULL, sepStr);
arr[count] = atoi(word);
}
free(tempStr);
}

int main(void){
// マスの数を取得する
char str[BUFFER_SIZE];
fgets(str, BUFFER_SIZE, stdin);
removeReturnCode(str);
const int numOfCells = atoi(str);
const int goal = numOfCells – 1;
const int minCells = 2;
const int maxCells = 100;
if (numOfCells < minCells || maxCells < numOfCells) {
return EXIT_FAILURE;
}

// マスを生成する
fgets(str, BUFFER_SIZE, stdin);
removeReturnCode(str);
int cells[numOfCells];
separateStringToArray(str, cells, numOfCells);
const int minCellNum = -100;
const int maxCellNum = 100;
for (int idx = 0; idx < numOfCells; idx++) {
if ( ( (idx == 0 || idx == (numOfCells – 1) ) && cells[idx] != 0) ||
(cells[idx] < minCellNum || maxCellNum < cells[idx]) ) {
return EXIT_FAILURE;
}
}

// 出目の回数を取得する
fgets(str, BUFFER_SIZE, stdin);
removeReturnCode(str);
const int acts = atoi(str);
const int minActs = 1;
const int maxActs = 100;
if (acts < minActs || maxActs < acts) {
return EXIT_FAILURE;
}

const int minActNum = 1;
const int maxActNum = 100;

// 出目を回し、結果を出力する
for (int count = 0; count < acts; count++) {
fgets(str, BUFFER_SIZE, stdin);
removeReturnCode(str);
int num = atoi(str);
if (num < minActNum || maxActNum < num) {
return EXIT_FAILURE;
}

int moveLog[BUFFER_SIZE] = {0};
_Bool finished = false;
while (true) {
if (num == goal) {
finished = true;
break;
}else if (num < 1 || goal < num) {
finished = false;
break;
}else{
int moves = cells[num];
if (moves == 0) {
finished = false;
break;
}else{
num += moves;
_Bool infinity = false;
int idx;
for (idx = 0; idx < BUFFER_SIZE && moveLog[idx] != 0; idx++) {
if (num == moveLog[idx]) {
infinity = true;
break;
}
}
if (infinity) {
finished = false;
break;
}else{
moveLog[idx] = num;
}
}
}
}

if (finished) {
puts("Yes");
}else{
puts("No");
}
}

return EXIT_SUCCESS;
}[/code]

解説ほか

基本的にやっていることはC++でのコードとほぼ同様のことである。ただし、C++で使えた可変長配列や文字列オブジェクトなどは使えないため、それぞれ代替の処理に置き換えている。とりあえず、異常処理系もある程度想定して書いたつもりである。

結果

cf: https://paiza.jp/poh/joshibato/kirishima/result/c8ae1056

全テストケース通過、処理時間も0.01秒だった。

最後に

これまでPOH6のそれぞれのミッションをC++で書いた場合、C言語で書いた場合の回答例と簡単な解説を挙げた。まだ不十分な点はあるものの、もしそれぞれのミッションがわからない場合は、是非当方のコードを参考にして挑戦してみてもらいたい。

ウェブマスター。本ブログでITを中心にいろいろな情報や意見などを提供しております。仕事依頼や相談などについては、Contact Formよりお願いいたします。

コメント

  1. sasaki より:

    ああ、すみません…-1ではなく+1ですね。そうなんです。strlen(str)+1から+1をなくしても問題なく動作したので疑問に思い質問致しました。-1は色々いじった後の手元のコードを見て誤記ってしまったようです。すみません。ご丁寧にご回答頂きありがとうございました。

  2. sasaki より:

    突然のコメント失礼致します.佐々木と申します.
    コードを拝見しました際,いくつか気になる点が出てきましたので質問させてください.(見当違いの質問があれば申し訳ありません.)

    ・12行目のstr[strlen(str) – 1] = ‘\n’;で改行文字を代入しているのは何故でしょうか.(改行文字の削除を意図している関数であれば’\0’を代入するのかと思ったのですが,なにか意図のある実装なのでしょうか.)

    ・23行目のstrncpyの第3引数のstrlen(str)-1で-1しているのは何故でしょうか.(-1は不要なように思うのですが,なにか意図のある実装なのでしょうか.)

    • SAITO Tomomi より:

      >佐々木様
      コメントありがとうございます。回答いたします。

      12行目について:
      これは明らかに当方のミスです(本来ならNULL文字を代入しなければならないが、ここでは改行コードになってしまっている)。追って修正いたします。ご指摘ありがとうございます。

      23行目について:
      これはstrlen(str)+1ですね。これはstrlenを使った場合、NULL文字は文字列の長さに含まれないことへの対策を意図しております。今回はcallocを使っているため+1がなくても実装上問題はありませんが、mallocを使った場合は問題が発生するかもしれません。

      以上の通りです。