はるのぶろぐ。

情報系大学生ハルが、ゆるゆるとIT関係についてや日々の雑記を綴ります。ちょっとだけ、あなたの役に立てる、そんなブログを目指しています。

【AtCoder】超初心者のためのモノグサプログラミングコンテスト2022B問題の解説【C言語】【ABC238】

参加してます!ポチッとお願いします

記事名と
URLをコピー

こんにちは、情報系大学生のハル(Blog_IT_haru)です。

今回は、C言語AtCoder(ABC238)に参加したので、自分の復習を兼ねてまとめていきたいと思います。

今回は、B問題をやっと理解し、ACを取ることができたので、B問題についてです。

C言語で解説している人がいなくて、自己流でやったので、色々おかしいところがあるかもしれません笑

f:id:Blog_IT:20220207102012p:plain

問題

B問題 Pizza

最初に、円の中心から 12 時の方向に切れ込みをひとつ入れます。

次に、以下の操作を N 回繰り返します。

i 回目の操作では以下を行います。

  1. まず、ピザを時計回りにAi度回転させる。
  2. 次に、円の中心から12時の方向に切れ込みをひとつ入れる。

制約:入力は全て整数
1≦N≦359,1≦Ai≦359
同じ場所に複数回切れ込みが入ることはない。

入力は操作の回数であるNと、角度の値である、Aiを受け取る。

出力は、一番大きい角度の値を出力する。

引用:B - Pizza

正解率

正解率は以下になります。

この問題が解ければ、灰コーダーから茶コーダーへ行けるのではないでしょうか。

灰コーダー正解率:54.4 %
茶コーダー正解率:84.3 %
緑コーダー正解率:89.8 %

引用:【AtCoder解説】PythonでABC238のA,B,C,D,E問題を制する! - Qiita

 

考え方

ちょっと複雑すぎるかもしれませんが、一応こんな感じで考えました。

  1. Nとaをscanfで読み取る。
  2. 初めに0°=360°のところに切れ込みがあるから、配列bの添字0、N+1の位置に、それぞれ0と360の値を入れる
  3. ichiは360°を入れる
  4. 360°からどれくらい動いたのかを変数ichiに入れる
  5. ichiの値を配列cに保存する。このとき配列cには、今どこに切れ込みがあるかが入っている。
  6. 配列bには、添字0とN+1の位置にはもう値が入っているので、添字1~Nのところに、切れ込み位置が保存されている配列cの値を入れていく。
  7. 配列bに切れ込み位置がすべて入ったので、バブルソートでソートする。
  8. 角度は、b[i+1]-b[i]で求めることができるので、それで求める。
  9. kotaeに0を入れ、角度の最大値を求める。

私のコード

#include <stdio.h>
int main(void){
    int N=0;
    scanf("%d\n",&N);
    int a[N];
    for(int i=0;i<N;i++){
        scanf("%d ",&a[i]);
    }
    int b[N+2];
    b[0] = 0;
    b[N+1] = 360;
    int ichi = 360;
    int c[N];
    for(int i=0;i<N;i++){
        if(ichi < a[i]){
            ichi = ichi+360-a[i];
            c[i] = ichi;
        }
        else{
            ichi = ichi - a[i];
            c[i] = ichi;
        }
        b[i+1]=c[i];
    }
    int temp=0;
        for (int i = 0;i < sizeof(b)/sizeof(b[0]) - 1;i++) {
            for (int j = 0;j < sizeof(b)/sizeof(b[0]) - 1;j++) {
                if (b[j + 1] < b[j]) {
                    temp = b[j];
                    b[j] = b[j + 1];
                    b[j + 1] = temp;
            }
        }
    }
    int kakudo[N+1];
    for(int i=0;i<N+1;i++){
        kakudo[i] = b[i+1]-b[i];
    }
    int kotae=0;
    for(int i=0;i<N+1;i++){
        if(kotae<kakudo[i]){
            kotae=kakudo[i];
        }
    }
    printf("%d\n",kotae);
}

必要な知識

以下をクリックしていただければ、該当する部分に飛ぶことが出来ます。

A問題と同様の知識↓

for文の使い方

同じ処理を繰り返したいときに使用する関数です。

似たような機能をもつ関数として、while文もありますが、今回は、省略します。

for文は、以下のように記述します。

今回はi=0から、i<5のときfor文の中身である足し算を繰り返すという処理を行いました。

より詳しく知りたい方は、こちらからどうぞ。

繰り返しを行う文 - 苦しんで覚えるC言語

ソートの仕方

今回は、バブルソートを用いてソートを行います。

バブルソートの詳しい使い方は以下に載っています。

C言語でバブルソート - ハルの初心者プログラミング部

コードは以下のような感じが基本のバブルソートになります。

aが実際にソートする配列になります。

int temp;
int i,j;
        for (i = 0;i <sizeof(a)/sizeof(a[0]) - 1;i++) {
        for (j = 0;j < sizeof(a)/sizeof(a[0]) - 1;j++) {
            if (a[j + 1] < a[j]) {
                temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }

最大値の求め方

最大値の求め方は、以下の記事で書いていますが、基本的な考え方の部分は以下のコードのような感じです。

C言語で最大値を求める - ハルの初心者プログラミング部

aが実際にソートする配列になります。

int max = 0;
    int i;
    for(i=0;i<5;i++){
        if(max<=a[i]){
            max=a[i];
        }
    }

A問題でも使用した知識

scanfで値を読み取る方法

今回は、整数なので、以下の様に読み取ります。

int n;
scanf("%d",&n);

intは、型の種類の一つで、整数を取り扱います。

int型のサイズは4バイトで、最大値は2147483647、最小値は-2147483648となります。printf関数などを使って出力表示する際の変換指定子はdになります。

引用元:

【C言語入門】整数(int、long int、short int)の使い方 - 侍エンジニアブログ

このとき出てきた、%dのdが、変換指定子dです。

簡単に言うと、整数を読み取るためのものです。

scanf自体は、scanf(”書式文字列”, &変数名1, &変数名2, ・・・)というふうに使用します。(引用:【C言語入門】scanfで数値、文字列の入力(sscanfの使い方も解説) | 侍エンジニアブログ

書式文字列の中に、%dなどの変換指定子を書きます。

if文の使い方

過去に、if文について書いていた記事がありましたので、一部引用しました。

ifという文字通り、「~なら~する」という、条件で処理を実行するか否か決めることができます。

次のように使います。

または、

です。

(引用元:C言語で最大値を求める - ハルの初心者プログラミング部

ただ、私の考え方で解く場合はelse ifの知識も必要です。

以下のように使用します。

ifは最初の1つだけ、elseも最後の1つだけですが、else ifは何回でも使用することができます。

printfの使い方

過去に、printfについて書いていた記事がありましたので、一部引用しました。

printfは、文字列を表示するための関数です。

普通は、以下のように、文字列を記述するのですが、今回は整数の計算結果を記述したいので、少し違う書き方になりましたね。

普通の文字列の場合

整数の場合(今回)

このとき出てきた、%dのdが、変換指定子dです。

%d

printf内で使います。

整数を10進数で出力します。

int型に対応します。

使用例

この場合、10と出力されます。

\n

printf内で使います。

改行を行います。

先程の変換指定子の前か後に記入します。

前に記入した場合は前が改行され、後ろに記入した場合は後ろが開業されます。

今回も、見やすく出力するために、使用しました。

こんな感じですね。

(引用元:C言語で合計値を求める - ハルの初心者プログラミング部

配列

配列は、ちょっとわかりにくいかもしれませんが、分かればそんなに難しくはありません。

たとえば、int a[5];と宣言された配列があるとすると、これは、aというタンスに、番号が振られた引き出しが5つあることを示しています。

また、int a[5];と宣言されているので、その引き出しには、int型のものしか入りません。

Aというタンス↓

f:id:Blog_IT:20220207105015p:plain

a[0]~a[4]と添字が書かれた配列が存在します。

int a[5];と宣言しているので、添字は0,1,2,3,4です。

5つの引き出しがありますが、a[5]という添字の入れ物は無いことに注意しましょう。

int a[5] = {1,2,3,4,5};と宣言した場合、a[0]には1が、a[1]には2が、といった感じでそれぞれ値が入っています。

ただし、AtCoderなど、入力する値がある場合は、{}のように事前に値を入れずに、以下のようにして配列を使うことが多いです。

事前に値を入れておくことを、初期化と言います。

    int a[5];
    for(int i=0;i<5;i++){
        scanf(“%d”,a[i]);
    }

これはfor文でi、すなわち添字を0から4まで増やしていき、値を入れているということです。

この場合、入力が1 2 3 4 5とされていれば、値がそれぞれ入っていきます。

ただ、スペースが有る場合、scanfの%dの後ろにスペースを開けないといけませんのでご注意を。

配列について、もう少し詳しい説明が知りたい方は、以下のサイトがおすすめです。

配列の使い方 - 苦しんで覚えるC言語 [MMGames]

おすすめな記事

ABC238のA問題とC問題の解説も書いているので、ぜひ読んでみてください。

blog-it.hatenablog.com

blog-it.hatenablog.com

まとめ

いかがでしたか?

少し分かりづらく、複雑なコードになってしまいましたが、一応これでACを取ることができます。

ただ、C言語でもう少しスマートに書くことができるのであれば、コメントから教えてくれると嬉しいです。

\ほんとの初心者の方は、本で学ぶのもおすすめ!/

以下の記事でおすすめ本紹介しています!

blog-it.hatenablog.com

この記事がいいな、と思ってくれたら、SNSなどで拡散したり、

ブックマークやコメントなどしてくれると励みになります!

下の方とサイドバーにある、サポートもお待ちしています!

更に、読者になってくれたら、お返しに私も読者になります!

また、この記事の内容についてなにかありましたら、

お問い合わせ、コメント、TwitterのDMなどによろしくお願いします。

それでは。