AtCoder Beginner Contest A問題の解き方(C言語)

プログラミングって敷居高いよね!
ということで敷居を少しでも下げられるように稚拙ですが記事を書きました。(最後に紹介するもののまとめ、ミスしがちな場所まとめを追加しました。)

まず第一にAtCoderのアカウントを作成しましょう。
https://atcoder.jp/?lang=ja

ここでAtCoderで覚えておくとよい超基本用語を書いておきます。

AC、Acceptedの略で正解の意味

WA、WrongAnswerの略で不正解の意味

ABC、AtCoderBeginnerContestの略で基本的に初心者はこれを受ける。
A問題B問題C問題D問題があり難易度は基本A<B<C<D

ARC、AtCoderRegularContestの略で基本的に中級者はこれを受ける。
C問題D問題E問題F問題があり難易度は基本C<D<E<F

AGC、AtCoderGrandContestの略で基本的に中級者から上級者が受ける。
A問題B問題C問題D問題E問題F問題があり難易度は基本A<B<C<D<E<F
⚠️AGCはとても難しくA問題でもABCのC問題程度の難しさです。

パフォーマンス、受けたコンテストそれぞれにつきます。
多く、早く解くとパフォーマンスは上昇します。

レート、今まで受けたコンテストの結果(パフォーマンス)によって決められる値です。
400毎に色が変わり
灰→茶→緑→水色→青→黄→橙→赤
となっていきます。
そして競技プログラマーを色を用いて
灰コーダー茶コーダー緑コーダー……などと言います。(ただし赤だけレッドコーダーと言われたりします。)
ABCではレート1200が限界でそれ以上レートをABCの結果で上げられず
ARCではレート2800が限界でそれ以上レートをARCの結果で上げられません。
AGCは無制限です。






AtCoderの情報はここまでにして、ではさっそくHello Worldを出力するプログラムを書いてみましょう。

と言われてもプログラムってどこに書けばいいの?とかプログラミング環境の準備って難しいと思う人も多いと思うのでとりあえずオンラインでプログラムをかけるpaiza.ioを紹介します。https://paiza.io/en/projects/new?language=c
これで慣れてきたらVisualStudioなどをダウンロードしてプログラムを書くことをオススメします。



#include <stdio.h>
int main(){
	printf("Hello World");
}

#includeというのは例えるなら工具箱の用意で
stdio.hっていう道具箱の中から道具を使いますよというような意味があります。(最初のうちはstdio.hのみで良い。studio.hと書かないように気をつけましょう。)

にint main(){}ですがこれはmain関数と言われるものでC言語ではこの関数からスタートします。
A問題ではほかの関数を使う機会はあまりないため特に気にする必要はないかと思います。

このprintf(″″);の中に入っている文が出力されるので中身を違う文にすると当然違う文が出力されます。
AtCoderのA問題ではYESとかNOとかを答える時にこのprintf(″″);を用います。

そして基本的に文末にはセミコロン(;)を用います。
説明時には省くこともありますがプログラム例ではちゃんとセミコロンを書くのでそれを見て慣れていってください。


これだけではまだA問題は解けません。

次は変数についてです。
変数というのはよく箱に例えられるもので数値、文字や文字列を入れることが出来ます。
しかし、数値を入れておける変数と文字や文字列を入れておける変数は違うので使い方には気をつけないといけません。
ではプログラム例を載せます。

#include <stdio.h>
int main(){
	char str;
	char str2[256] = "Hello World";
	int a;
	
	str='z';
	
	a=57577;
	
	printf("%c\n",str);
	
	printf("%s\n",str2);
	
	printf("%d\n",a);
}

出力

z
Hello World
57577

となります。

このプログラムをとりあえずひとつひとつ解説していきます。
まずchar str、これはchar型(文字や文字列を扱う型)の変数str(strは変数の名前で自分で勝手に決められる)を用意するという意味で、変数の宣言と言われるものです。
ただしこのstrにはたった1文字しか入れることは出来ません。
じゃあ文字列を入れるにはどうすればいいのかそれが次に紹介するものです。

char str2[256]、これはstr2[0]からstr[255]までの256個の変数を用意するという意味です。
すると、先程紹介した1文字しか入れられなかった変数を256個用意する訳ですから256文字まで入れられる……という訳では無いのです。
実は文字列には文字列の終わりを表す文字(ヌル文字)が必要でその文字を入れるために1つ使える変数が減ります。そのため255文字までしか入れることができません。
これはバグの温床なので気をつけましょう。
さらに言うとこの宣言の際[]の中に変数入れないようにしましょう。
また、これの横に=″Hello World″;とありますが、これは実際にstr2にHello Worldを代入するという意味があります。(これだけ後で代入するのがやや面倒臭いので先に代入しています。)

int a、これは整数を入れられる変数(int型)aを用意するという意味です。

str=′z′;、これは変数strにzを代入するという意味です。(1文字は基本的に″じゃなくて′を用いましょう)←先程のchar str2[256] = "Hello World";がchar str2[256] = "H";と1文字だったとしてみよう。
この時は1文字とは言え文字列なので″を用いることとなります。

a=57577;、これは変数aに57577を代入するという意味です。
これは整数を代入するため′や″は必要ありません。

printf("%c\n",str);、これは最初に説明しました。
しかし、中に先程とは違うものが入っているのでそれを説明します。
まず変数strにはzが代入されていますが、printf("str");
と入れると出力はstrとなってしまいます。
printf("str");はただただstrという文字を出力するだけで人間の気持ちを汲んでzを出力してはくれません。
そこでちゃんとzを出力するためにprintf("%c\n",str);を用います。イメージで言うと%cは1文字を入れられる欄を作ってくれて,の後のstrをその欄に入れると言った感じです。
最後に\nは改行を意味します。
AtCoder初期のA問題には出力の最後に改行を入れないと間違いと判定されるようになっている場合がありますので気をつけてください。

次の2行もほとんど同じなのですが、%dは整数(int型)%sは文字列の時に用いるものです。

似たようなプログラムを載せておきます。

#include <stdio.h>
int main(){
	char str[256]="Hello";
	char str2[256] = "World";
	
	printf("%s %s\n",str,str2);
	
	str[1]='a';
	str2[1]='e';
	str2[2]='e';
	str2[3]='n';
	str2[4]=' ';
	
	printf("%s%s",str,str2);
}

出力

Hello World
HalloWeen

となります。
これはstrにHello、str2にWorldを代入し
後でstrの2文字目(str[1])str2の2,3,4,5文字目に違う文字を代入しています。(プログラムでは0から数字がスタートすることに注意しましょう)

次に演算です。

#include <stdio.h>
int main(){
	int a,b,c,d,e,f,g;
	a=33;
	b=4;
	c=a+b;
	d=a-b;
	e=a*b;
	f=a/b;
	g=a%b;
	printf("aの値は%dでbの値は%d\nその和は%d\nその差は%d\nその積は%d\nその商は%d\na÷bのあまりは%d\n",a,b,c,d,e,f,g);
}

出力

aの値は33でbの値は4。
その和は37
その差は29
その積は132
その商は8
a÷bのあまりは1

となります。


このプログラムではまず整数を入れられる変数a~gを用意しています。
そして今回はa=33、b=4として計算しています。
それぞれのc~gまでの演算は出力を見ていただけるとわかると思いますが、気をつけないといけない演算があります。
まず掛け算、これは×ではなく*を使います。
次に割り算、これは÷ではなく/を使います。また整数どうしの割り算では小数点以下が無視されます。
最後に余り、これは%で表現できます。

これ以外にも出来る演算はあり
例えば

#include <stdio.h>
int main(){
	int a;
	a=0;
	
	printf("%d\n",a);
	a++;
	printf("%d\n",a);
	++a;
	printf("%d\n",a);
	a--;
	printf("%d\n",a);
	--a;
	printf("%d\n",a);
	a+=5;
	printf("%d\n",a);
	a=a+5;
	printf("%d\n",a);
	a-=5;
	printf("%d\n",a);
	a=a-3;
	printf("%d\n",a);
	a*=10;
	printf("%d\n",a);
	a=a*10;
	printf("%d\n",a);
	a/=2;
	printf("%d\n",a);
	a=a/2;
	printf("%d\n",a);
	a%=13;
	printf("%d\n",a);
	a=a%5;
	printf("%d\n",a);
}

出力
0
1
2
1
0
5
10
5
2
20
200
100
50
11
1

となる。
このプログラムでは最初変数aの値は0で
a++によって+1されて1になる。
次に++aによって+1されて2になる。
次にa--によって-1されて1になる。
次に--aによって-1されて0になる。
次にa+=5によって+5されて5になる。
次にa=a+5によって+5されて10になる。
次にa-=5によって-5されて5になる。
次にa=a-3によって-3されて2になる。
次にa*=10によって×10されて20になる。
次にa=a×10によって×10されて200になる。
次にa/=2によって÷2されて100になる。
次にa=a/2によって÷2されて50になる。
次にa%=13によって÷13した時の余りの11になる。
最後にa=a%5によって÷5した時の余りの1になる。

そろそろA問題が解けるんじゃないか?と思うかもしれませんがまだ解けません。
なぜなら入力を受けとることが出来ないからです。
ということで次は入力の受け取り方を解説します。
プログラム例です。

#include <stdio.h>
int main(){
	int a;
	char str;
	char str2[256];
	scanf("%d",&a);
	scanf(" %c",&str);
	scanf("%s",str2);
	printf("%d ",a);
	printf("%c ",str);
	printf("%s ",str2);
}

入力

10 x AtCoder

出力

10 x AtCoder

となります。

このプログラムではscanfと言われるものが用いられています。
そしてscanfの内部の書き方はほとんどprintfと同じです。
しかし違うところもあります。
まず1つ目謎の&、これは文字列以外を読み込むときは入れてください。(逆に文字列の場合は入れないでください。)
この&は今のところは知らなくても大丈夫ですがアドレスというもののせいで入れなくてはいけません。
次2つ目%cの前の謎の空白、これは空白や改行は無視するという意味があります。
というのも文字を読み込む際空白も読み込んでしまい望み通りの入力処理ができない場合があるからです。
これはとてもバグの温床になるので気をつけましょう。

ここまで来たら解けるA問題が出てきます。
例えばhttps://beta.atcoder.jp/contests/abc012/tasks/abc012_1です。
下に答えは載せますが一度見ずに書いてみましょう。
提出もしてみてACが出たらOKです。









これの解答例は

#include<stdio.h>

int main() {
	int A,B;
	scanf("%d%d",&A,&B);
	printf("%d %d\n",B,A);
}

解説
この問題では入力された整数の順をひっくり返して出力すればよいです。
そのためscanfによってAとBの入力を受け取り、printfによってB→Aの順で出力します。

今回上手くいかなかった場合チェックすべきこと

  • 綴りはあっているか
  • scanfで&を忘れていないか
  • 最後に改行をちゃんと入れているか
  • printfの順は合っているか
  • セミコロン(;)を忘れていないか


次に解説するのはif文です。これが使えると多くのA問題を解くことが出来ます。
ではプログラム例です。

#include <stdio.h>
int main(){
	int a;
	scanf("%d",&a);
	if(a==30)
	{
		printf("=30");
	}else if(a<30)
	{
		printf("<30");
	}else
	{
		printf(">30");
	}
}

入力→出力

30→=30

20→<30

40→>30

このプログラムでは条件分岐if(){}を用いています。
()の中に条件を書き、それがtrue(正しい)であれば
{}の中の操作に移り、false(正しくない)であれば{}
の中は無視され次に移ります。
また複数の分岐の場合else if(){}を用いて繋げていきます。
elseではそれまでの条件を満たさなかった場合{}内の操作に進むという意味です。
では()の中に入れる主なものを説明していきます。

  1. a>bまたはb<a、aがbより大きい(a=bは含まない)
  2. a>=bまたはb<=a、aがb以上(a=bも含む)
  3. a==b、aとbが等しい(a=bはaにbを代入という意味なので間違わないように気をつけましょう。)
  4. a!=b、aとbが等しくない
  5. &&、二つの条件式を繋いでかつという意味を表す。例:if(a>=5&&a<=10){printf(″YES″);}aが5以上かつ10以下の時YESと出力
  6. | |、二つの条件式を繋いでまたはという意味を表す。例:if(a<=5||a>=10){printf(″NO″);}aが5以下または10以下の時にNOと出力

先程のプログラム例で言うと変数aの値が30だったらif文の中の{}に進みそれ以降のelse ifやelseは無視されます。
次にaの値が30より小さい値だったらif文の条件は満たさないのでifの{}内は無視されてelse ifに進みます。
else ifの条件はこの場合満たしているのでelse if文の中の{}に進みそれ以降のelseは無視されます。
最後にaの値が30より大きい値だったらif文の条件は満たさないのでifの{}内は無視されてelse ifに進みます。
更にelse if文の条件も満たしていないのでelse ifの{}内も無視されます。
そしてifとelse ifの条件を満たさなかったため最後のelseの{}内に進みます。

では、もう1つプログラム例を挙げることにします。今度は文字の比較です。

#include <stdio.h>
#include <string.h>
int main(){
	char str[11],str2[11];
	scanf("%s%s",str,str2);
	if(str[strlen(str)-1]==str2[0])
	{
		printf("しりとり成立");
	}else
	{
		
		printf("しりとり不成立");
	}
}

入力→出力
apple enter→しりとり成立

atcoder banana →しりとり不成立

このプログラムでは2つの文字列についてしりとりが成立しているかを判定できます。
ここで新しく登場したのが
string.hとstrlen()です。

ではまずstring.hについて、これはstrlen()を用いるために必要な工具箱のようなものです。(他にもstring.hをインクルード(用意)することで出来ることはありますがまずはstrlen()だけ覚えておけばよいです。)
次にstrlen()について、これはその文字列の長さ(何文字か)を取得する関数です。
今回の場合しりとりなので、当然1つめの文字列の最後の文字の情報が必要です。
プログラムで表現するとstr[文字列の長さ-1]の情報が必要なので文字列の長さを求めるstrlen()が必要となってくるわけです。(文字列の長さ-1の-1がなぜあるかと言うとプログラムの数字は0から数え始めるからです。具体的にappleの場合を言うとstr[0]にa、str[1]にp、str[2]にp、str[3]にl、str[4]にeが入れられます。この時最後の文字を取り出そうとするとappleという文字列の長さ5を用いてstr[5-1]と書けます。)

ここまでくるとかなりのA問題が解けると思います。

ではまずABC020 A問題
https://beta.atcoder.jp/contests/abc020/tasks/abc020_a
を解いてみましょう。







#include<stdio.h>
#include<string.h>

int main() {
	int n;
	scanf("%d",&n);
	
	if (n==1) {
		printf("ABC\n");
	}
	if (n == 2) {
		printf("chokudai\n");
	}
}

解説
解説はつけるまでもないと思いますが、とりあえず入力をscanf()で受け取ってそれが1ならABC、2ならchokudaiを出力します。
この問題はA問題の中でもかなり簡単な問題だと思うのでこの問題はパパッと解けるようになって欲しいです。
もしWA(不正解)が出たら最後の改行を忘れている可能性があります。

次はABC028のA問題
https://beta.atcoder.jp/contests/abc028/tasks/abc028_a
を解いてみましょう。




#include<stdio.h>
#include<string.h>

int main() {
	int n;
	scanf("%d",&n);
	if (n <= 59) { printf("Bad\n"); }
	else if (n <= 89) { printf("Good\n"); }
	else if (n <= 99) { printf("Great\n"); }
	else { printf("Perfect\n"); }
 
}

解説
テストは0点から100点までであって、その点数をscanfによって取得します。
その後if文によって59点以下かどうか判定し60点以上ならば次のelse ifに進みます。
else if文によって89点以下かどうか判定し90点以上ならば次のelse ifに進みます。
else if文によって99点以下かどうか判定し100点ならelseに進みます。
このような仕組みから60点以上、90点以上という条件をelse if文の中にわざわざ入れなくても良いことがわかります。(入れても大丈夫です。)

ではこの問題の誤答も載せておきます。

int main() {
	int n;
	scanf("%d",&n);
	if (n <= 59) { printf("Bad\n"); }
	if (n <= 89) { printf("Good\n"); }
	if (n <= 99) { printf("Great\n"); }
	if (n ==100) { printf("Perfect\n"); }

}

この誤答ではelse ifを用いずifのみで解こうとしています。
しかし、このプログラムではテストの点数が50点の場合出力が
Bad
Good
Great

と3つ出てしまいます。
このミスはif文がその前のif文の影響を受けないなため起こります。
つまり先程の正しいプログラムではif文の条件がfalse(間違い)の時だけ次のelse ifに進んでいましたがこの誤ったプログラムではif文の条件がfalseでもtrue(正しい)でも次のif文に進んでしまうということが原因です。
if文のみで正解するには2つめのif文に60以上という条件、3つめのif文に90以上という条件をつけるか、最後以外のif文の{}内の最後にreturn 0;(値を返すという操作で関数をそこで終わらせることが出来ます。main以外の関数を使わない場合値を返すこと自体の意味は気にしなくても良い)を付ける必要があります。

3問目はABC038のA問題
https://beta.atcoder.jp/contests/abc038/tasks/abc038_a






#include <stdio.h>
#include <string.h>

int main(){
	char str[120];
	
	scanf("%s",str);

	if(str[strlen(str)-1]=='T')
	{
		printf("YES");
	}else{
		printf("NO");
		}
}

解説
まず文字列を取得します。
そして、しりとりの例でやったように最後の文字
str[strlen(str)-1]を見てそれがTと等しい時YESと出力し等しくない時NOを出力します。



ここまでの解説でほとんどのA問題は解くことが出来ると思います。
では、最終問題としてちょっと頭をひねらないと解けないA問題を載せておきます。
https://atcoder.jp/contests/abc025/tasks/abc025_a






#include<stdio.h>

int main() {
	int n;
	char str[30];
	scanf("%s%d",str,&n);
       n--;
	printf("%c%c\n",str[n/5],str[n%5]);

}

解説
Sがabcdeだった場合
str[0]がa、str[1]がb、str[2]がc、str[3]がd、str[4]がeとなり
25個の文字列を辞書順に並べると
aa, ab, ac, ad, ae,
ba, bb, bc, bd, be,
ca, cb, cc, cd, ce,
da, db, dc, dd, de,
ea, eb, ec, ed, ee
となります。
ここで気がついてほしいのが1つめの文字が5個飛ばしで変わっているということです。
するとまた、2つめの文字が5で割ったあまりで表現出来ることが分かります。
これをプログラムで行うそれが
printf("%c%c\n",str[n/5],str[n%5]);なわけです。
しかし8番目で実験してみると答えはbcなのですが
str[8/5]=str[1]つまりb(整数の割り算では小数切り捨て)str[8%5]=str[3]つまりdとなりbdとなってしまいます。
これを直すのがprintfの前のn--;というわけです。
nが1小さくなるとズレが解消するのが分かると思います。(このズレは日常生活では1から数えるのに対しプログラムでは0から数えるため生じます。最初のうちは慣れないと思いますが、0から数えた方が便利なこともありいつかは自然と慣れるはずです。)
具体的にはstr[7%5]=str[2]となりcが出てきてくれます。


これでAtCoderBeginnerContest A問題の解き方解説を終わります。



オマケ
全てのおさらい(変数名は適当にa,b,cとします)

#include<>
最初に書く。道具箱の準備のようなもので、基本的にはを入れておく。

int main(){}
main関数、プログラムはこの関数からはじまり基本的にこの{}内にプログラムを記述する。

printf(″″);
中に文字を入れて出力できる。
変数を出力する時はprintf(″%d%c%s″,a,b,c);のように書く。この場合aは整数(%d)、bは文字(%c)、cは文字列(%s)″″の中に\nを入れると改行できる。

int a;
int型(整数を入れる変数)の宣言

char a;
char型(1文字を入れる変数)の宣言

char a
char型(文字列を入れる配列)の宣言
の中に必要な文字数+1以上を入れる(NULL文字に注意)n文字目はa[n-1]にしまわれる。

scanf();
入力を読み取ります。
scanf (″%d %c%s″,&a,&b,c);
aは整数、bは文字、cは文字列
空白や改行を読み込まないように%cの前は空白を入れましょう。
また文字列のみ&はいれない。

aにbを代入a=b

足し算a+b
引き算a-b
掛け算a*b
割り算a/b(少数以下切り捨て)
あまりa/b

c=a演算子bのように代入と合わせて使いましょう


1足すa++や++a
1引くa--や--a
b足すa+=b
b引くa-=b
bかけるa*=b
bで割るa/=b
bで割ったあまりa%=b

基本的に代入と合わせて使わない。
a++ならa=a+1
a+=bならa=a+bという意味が既にあるため。

if(){}
条件分岐できる。()内に条件{}内に条件満たした時に行う動作を入れる。
多数の分岐の場合else if(){}と繋げる。
if文にelse if文を繋げた場合どれか条件を満たした時点でそれ以降のelse if文は無視される。
else{}を繋げた場合、それまでのif文else if文の条件が満たされなかった時のみ{}内に進む。

条件

  1. a>bまたはb<a、aがbより大きい(a=bは含まない)
  2. a>=bまたはb<=a、aがb以上(a=bも含む)
  3. a==b、aとbが等しい(a=bはaにbを代入という意味なので間違わないように気をつけましょう。)
  4. a!=b、aとbが等しくない
  5. &&、二つの条件式を繋いでかつという意味を表す。例:if(a>=5&&a<=10){printf(″YES″);}aが5以上かつ10以下の時YESと出力
  6. | |、二つの条件式を繋いでまたはという意味を表す。例:if(a<=5||a>=10){printf(″NO″);}aが5以下または10以下の時にNOと出力

strlen(a)
#includeを最初に書いておく、するとstrlen()が使える。
これによって文字列aの長さが分かる。




ACが出なかった時に確かめるべきこと

  • 問題を読み間違ってないか
  • 綴りはあっているか
  • scanfで&を忘れていないか
  • printfで&を入れてないか
  • 最後に改行をちゃんと入れているか
  • セミコロン(;)を忘れていないか
  • 空白や改行を読み込んでないか(%cの前に空白を開けると解消します。)
  • if文の()の中に等号成立のつもりで代入の=を使ってないか
  • 括弧はちゃんと閉じているか
  • char str[100];などの時1から数えてないか
  • NULL文字のことを忘れてないか(100文字入れる時にchar str[100]にしていないか)