AOJをC#で解く③~ITP1_3~

#はじめに
前回に引き続きAOJの解説です。
前回の→[AOJをC#で解く②~ITP1_2~](https://qiita.com/hikarutakenouch/items/3bd7caf05448b279f0f6)

今回は[ITP1_3](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/3)です。
ITP1_3は繰り返し処理がテーマです。for文やwhile文が出てきます。

##A問題 - [Print Many Hello World](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/3/ITP1_3_A)
**問題**
1000 個の "Hello World" を出力するプログラムを作成して下さい。


**制約**
なし

**数値例**
*output*
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
.
.
.
.
.
.
Hello World

---

**解法**
for文で1000回回すだけです。
for文の()内は `(初期化式; 条件式; 更新式)`を書きます。言い換えると、繰り返す処理において、最初の状態：何回繰り返すか：繰り返す時に行う初期値への変更です。

```ITP1_3_A.cs
using System;

namespace ITP1_3_A
{
    class Program
    {
        static void Main()
        {
            for (int i = 0; i < 1000; i ++)
            {
                Console.WriteLine("Hello World");
            }
        }
    }
}

```

**コメント**
簡単な処理なので今回は特にないです。

##B問題 - [Print Test Cases](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/3/ITP1_3_B)
**問題**
１つの整数 x を読み込み、それをそのまま出力するプログラムを作成して下さい。

ただし、この問題は以下に示すようにいくつかのデータセットが与えられることに注意して下さい。
x が 0 のとき入力の終わりを示し、このデータセットに対する出力を行ってはいけません。

**制約**

- 1 ≤ x ≤ 10,000
- データセットの数は 10,000 を超えない。


**数値例**
*input*
3
5
11
7
8
19
0

*output*
Case 1: 3
Case 2: 5
Case 3: 11
Case 4: 7
Case 5: 8
Case 6: 19

---

**解法**
for文を使ったパターンとwhile文を使ったパターンを載せました。

*【for文を使った場合】*

for文で文字列を読み込み、xが0になったときにbreakさせるようにします。iをCaseのナンバリングにするため、1からスタートさせ、読み込む数値をくっつけてそのまま出力させます。

以前も少し触れましたが、`("Case {0}: {1}", i, x);`という書き方はフォーマット出力というやり方です。

```ITP1_3_B.cs
using System;

namespace ITP1_3_B
{
    class Program
    {
        static void Main()
        {
            for (int i = 1; i > 0; i ++)
            {
                int x = int.Parse(Console.ReadLine());
                if (x == 0) break;
                else Console.WriteLine("Case {0}: {1}", i, x);
            }

        }
    }
}

```

*【while文を使った場合】*

while文の()内は条件式です。つまりその式が真である限り永遠にwhile内の処理を繰り返します。ここではxが0の時処理が終わるので、xが0じゃない時に処理を繰り返すようにしたいので、`i != 0`が入ります。

while文の条件式でiを使いたかったので、先に0以外の数値を適当に入れて宣言しときます。while文内で宣言しても使えないので。

```ITP1_3_B.cs
using System;

class Program
{
    static void Main()
    {
        var count = 1;
        var i = 1;

        while (i != 0)
        {
            i = int.Parse(Console.ReadLine());
            if (i != 0) Console.WriteLine("Case {0}: {1}", count, i);
            count ++;
        }
    }
}

```


**コメント**
for文のほうが書きやすいですが、while文でも書けるようになっておくといいと思います！


##C問題 - [Swapping Two Numbers](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/3/ITP1_3_C)
**問題**
２つの整数 x, y を読み込み、それらを値が小さい順に出力するプログラムを作成して下さい。
ただし、この問題は以下に示すようにいくつかのデータセットが与えられることに注意して下さい。

x と y がともに 0 のとき入力の終わりを示し、このデータセットに対する出力を行ってはいけません。


**制約**

- 0 ≤ x, y ≤ 10,000
- データセットの数は 3,000 を超えない。


**数値例**

*input*
3 2
2 2
5 3
0 0

*output*
2 3
2 2
3 5

---

**解法**
こちらの問題も二通りのせておきます。なおどちらもwhile文を使い、どちらもxとyが0になったときにbreakさせます。

*【条件分岐させるパターン】*
文字列を読み込んだあとに、if文でどちらが大きいか判定し、出力させます。

```ITP1_3_C.cs
using System;

class Program
{
    static void Main()
    {
        while (true)
        {
            string[] a = Console.ReadLine().Split(' ');
            int x = int.Parse(a[0]);
            int y = int.Parse(a[1]);

            if (x == 0 && y == 0) break;
            else if (x <= y) Console.WriteLine("{0} {1}", x, y);
            else Console.WriteLine("{0} {1}", y, x);
        }
    }
}

```

*【Mathクラスのメソッドを使うパターン】*
`Math.Min(x, y)`でxとyの小さい方を判定、`Math.Max(x, y)`でxとyの大きい方を判定します。これを使って出力させるやり方であればわざわざif文を書く必要はなくなります。

```ITP1_3_C.cs
using System;

namespace ITP1_3_C
{
    class Program
    {
        static void Main()
        {
            while (true)
            {
                string[] i = Console.ReadLine().Split(' ');
                int x = int.Parse(i[0]);
                int y = int.Parse(i[1]);

                if (x == 0 && y == 0) break;
                Console.WriteLine("{0} {1}", Math.Min(x, y), Math.Max(x, y));
            }
        }
    }
}

```
**コメント**
一つの問題に対して、いくつか解き方があるので解けた後、他の人の回答をいろいろ見ていくとかなり勉強になると思います。


##D問題 - [How Many Divisors?](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/3/ITP1_3_D)
**問題**
３つの整数 a、b、cを読み込み、aから bまでの整数の中に、cの約数がいくつあるかを求めるプログラムを作成してください。

**制約**

- 1 ≤ a, b, c ≤ 10000
- a ≤ b


**数値例**
*input* : 5 14 80
*output* : 3

---

**解法**
まず約数はNを割り切る整数のことですね。例えば、6の約数は1, 2, 3, 6です。
ここで求めたいのは、

- cの約数

かつ

- a から b の間にある

整数が何個あるか、です。

よって、cをa~bの数字で割っていき、割り切れた回数を数えれば解けます。


```ITP1_3_D.cs
using System;

class Program
{
    static void Main()
    {
        string[] i = Console.ReadLine().Split(' ');
        int a = int.Parse(i[0]);
        int b = int.Parse(i[1]);
        int c = int.Parse(i[2]);
        var count = 0;

        for (int x = a; x <= b; x ++)
        {
          if (c % x == 0) count ++;
        }
        Console.WriteLine(count);
    }
}

```

*【LINQを使った解法】*
あまり見慣れないですが、LINQを使うとこうなります。
int型の配列xを宣言し、読み込んだ文字列を`Split`で空白で分割し、stringからint型に変換するという処理を`Array.ConvertAll`で全要素に対して適用します。
`int[] x = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();`でも同じ結果になります。

これを`Enumerable.Range`を使って、x[0]から順番に、(x[1] - x[0] + 1)個目までの数のうち、x[2]を割り切れる数を`Count`させます。

`Enumerable.Range`の()内は(a, b)でaからbまでではなく、aから数えてb個までなので、上記のような書き方になります。


```ITP1_3_D.cs
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] x = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
        //int[] x = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();でもよい
        Console.WriteLine(Enumerable.Range(x[0], x[1] - x[0] + 1).Count(q => x[2] % q == 0));
    }
}
```


**コメント**
「cの約数」かつ「a から b の間にある整数」という解の性質を出せれば、問題を解く手順は自ずと見えてくる問題だったと思います。文法よりも問題の定義をしっかりできるかどうかが肝要です。



#最後に
問題の難易度や処理のレベルが上がると、いくつか解法があるというパターンがあります。
その時にどっちをなんで選択したかをしっかり言語化できるといいと書きながら自分で思いました。状況に応じてどっちが向いてるなどがあると思うので。