Problem:
福縞軒

「福縞軒」は行列のできる人気のラーメン屋です。でも最近、お客さんの間から「待ち時間が長いのに、店に入ったら空席があるのは許せない」という声が聞こえるようになってきました。どうしてそんな不満が出るのか調べたいのですが、お店が開いているあいだは忙しくて、実際の行列の様子を調べることができません。しかし、長年の経験からお客さんが来る間隔や人数は分かっているので、それをもとに待ち時間の分析をすることにしました。

店内にはカウンターに向かって 17 の席があります。開店時間は正午で、お客さんは次のようにやってきます。

0 番から 99 番までの 100 組のグループが来ます。

i 番目のグループは正午から 5i 分後にお店に到着します。

i 番目のグループの人数は i % 5 が 1 のとき 5 人、それ以外のときは 2 人です。

(x % yは x を y で割ったときの余りを表わします。)

i 番目のグループは、席に着くと 17(i % 2) + 3(i % 3) + 19 分間で食事を済ませます。

最初の 10 グループの到着時刻、人数、食事時間は次のようになります。

グループ番号

0

1

2

3

4

5

6

7

8

9

到着時刻(分後)

0

5

10

15

20

25

30

35

40

45

人数(人)

2

5

2

2

2

2

5

2

2

2

食事時間(分)

19

39

25

36

22

42

19

39

25

36

また、お客さんを席に案内するときには、次のようにしています。

席には 0 から 16 までの番号が付いています。

x 人のグループは連続して x 個あいている席があった時だけ着席できます。

また、座れる場所が複数あった場合は、席の番号が最も小さくなるところに座ります。例えば、0、1、2、4、5番の席だけが空いていた場合、5 人のグループは着席できません。2 人のグループであれば 0、1 番に着席します。

一度着席したら、席を移動してもらうことはしません。

お客さんは 1 分単位で出入りします。各時刻には次の順序でお客さんを案内します。

前のグループの離席と同時に次のグループの着席が可能となります。

お客さんを着席させる際には、行列の先頭にいるグループから順に、できる限り多くのグループを同じ時刻に着席させます。行列の順序を追い越すことはしません。つまり、先頭のグループが着席できなければ、行列内の他のグループが着席できたとしても、着席させません。

その時刻に到着したグループは、行列が残っている場合は行列の最後尾に並びます。行列が無く、着席できる場合は着席し、できない場合は並んで待ちます。例として最初の 10 グループが到着するまでの様子を示すと以下のようになります。各行の3つの欄は、左から時刻、座席の様子、行列の様子を示しています。座席は「_」が空席で、番号はその席にその番号のグループが座っていることを示しています。

時刻: 座席 行列
0: 00_______________:
5: 0011111__________:
10: 001111122________:
15: 00111112233______:
18: 00111112233______:
19: __111112233______:
20: 44111112233______:
25: 4411111223355____:
30: 4411111223355____: 66666 グループ6が到着
34: 4411111223355____: 66666
35: 4411111__3355____: 6666677 グループ7が到着
40: 4411111__3355____: 666667788 グループ8が到着
41: 4411111__3355____: 666667788
42: __11111__3355____: 666667788
43: __11111__3355____: 666667788
44: 6666677883355____: グループ6、7、8が着席
45: 666667788335599__: グループ9が到着、着席

例えば、時刻 40 では 8 番目のグループが到着しますが、着席できないので行列に加わります。4 番目のグループは時刻 41 まで食事をします。時刻 42 では、4 番目のグループの席が空きますが、連続した席数が足りないので 6 番目のグループはまだ着席できません。1 番目のグループは時刻 43 まで食事をします。時刻 44 で 1 番目のグループの席が空くので、6 番目のグループが着席し、同時に 7 番目、8 番目のグループも着席します。9 番目のグループは時刻 45 で到着し、席が空いているのでそのまま着席します。

これらの情報を基にして、0 以上 99 以下の整数 n を入力として、n 番目グループのお客さんが待つ時間(分単位)を出力するプログラムを作成してください。

Input

複数のデータセットが与えられます。各データセットは１つの整数 n からなります。

データセットの数は 20 を超えません。

Output

各データセットについて、n 番目のお客の分単位の待ち時間(0 以上の整数)を１行に出力してください。

Sample Input

5
6
7
8

Output for the Sample Input

0
14
9
4

Solution in JAVA (judge verdict: Accepted)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class Main{

    public static void main(String[] args) throws NumberFormatException, IOException {

        exec();
    }

    private static void exec() throws NumberFormatException, IOException {

        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        String line; // 引数行

        while ((line = br.readLine()) != null) {

            int n = Integer.parseInt(line);
            int baseTime = 0; // 基準時間
            int addGroupNo = -1; // 入店したグループNo
            int groupMember = 0; // 入店したグループ人数
            int blankStartSeatNo = -1; // 空席No

            // 座席状態の初期化
            Map<Integer, GroupStatus> seatStatusMap = initSeatStatus();

            // 待ち行列の初期化
            LinkedList<GroupStatus> queueStatusList = new LinkedList<GroupStatus>();

            while (addGroupNo < n) {

                // 座席の状態更新
                updateSeatStatus(seatStatusMap, baseTime);

                // 待ち行列の状態更新
                updateQueueStatus(queueStatusList, baseTime);

                int waitGroupSize = queueStatusList.size();

                for (int i = 0; i < waitGroupSize; i++) {

                    // 待ち行列の先頭グループ人数取得
                    groupMember = queueStatusList.peek().groupMember;

                    // 空席シートNoの取得
                    blankStartSeatNo = getBlankStartSeatNo(seatStatusMap, baseTime, groupMember);

                    if (blankStartSeatNo >= 0) {

                        // 座席に先頭グループ追加
                        addGroupNo =
                            addSeat(seatStatusMap, queueStatusList.peek(), baseTime,
                                blankStartSeatNo, n);

                        // 待ち行列の先頭グループ削除
                        queueStatusList.remove();

                    } else {

                        // 座れる座席なし
                        break;
                    }
                }

                baseTime++;
            }

        }

    }

    /** n番目のグループ人数 */
    private static int getGroupMember(int i) {

        int result = i % 5;

        // i%5 の結果が1であれば5を、1でなければ2を返却
        if (result == 1) {
            return 5;
        } else {
            return 2;
        }
    }

    /** n番目のグループの食事時間 */
    private static int getMealTime(int i) {

        int result = (17 * (i % 2)) + (3 * (i % 3)) + 19;
        return result;
    }

    /** 待ち行列の状態更新 */
    private static void updateQueueStatus(LinkedList<GroupStatus> queueStatusList, int time) {

        // 待ち行列の更新
        for (GroupStatus queueStatus : queueStatusList) {

            queueStatus.endStayTime = time;
        }

        // 待ち行列の追加
        if (time == 0) {

            // 初回
            int groupNo = 0;
            int groupMember = getGroupMember(groupNo);

            GroupStatus queueStatus = new GroupStatus(groupNo, groupMember, time, time, true);
            queueStatusList.offer(queueStatus);

        } else if (time % 5 == 0) {

            // 初回以降
            int groupNo = time / 5;
            int groupMember = getGroupMember(groupNo);

            GroupStatus queueStatus = new GroupStatus(groupNo, groupMember, time, time, true);
            queueStatusList.offer(queueStatus);
        }
    }

    /** 座席状態の初期化 */
    private static Map<Integer, GroupStatus> initSeatStatus() {

        Map<Integer, GroupStatus> seatStatusMap = new HashMap<Integer, GroupStatus>(17);
        seatStatusMap.put(0, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(1, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(2, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(3, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(4, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(5, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(6, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(7, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(8, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(9, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(10, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(11, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(12, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(13, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(14, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(15, new GroupStatus(-1, 0, 0, 0, false));
        seatStatusMap.put(16, new GroupStatus(-1, 0, 0, 0, false));

        return seatStatusMap;
    }

    /** 座席状態の更新 */
    private static void updateSeatStatus(Map<Integer, GroupStatus> seatStatusMap, int time) {

        for (int i = 0; i < 17; i++) {

            if (seatStatusMap.get(i).isStay) {

                // 座席に客がいる場合

                seatStatusMap.get(i).endStayTime = time;

                if ((seatStatusMap.get(i).endStayTime - seatStatusMap.get(i).startStayTime) == getMealTime(seatStatusMap
                    .get(i).groupNo)) {

                    // 滞在時間から着席時間を減算した値が食事時間と一致した場合

                    // 座席ステータスを初期化
                    seatStatusMap.get(i).groupNo = -1;
                    seatStatusMap.get(i).groupMember = 0;
                    seatStatusMap.get(i).startStayTime = 0;
                    seatStatusMap.get(i).endStayTime = 0;
                    seatStatusMap.get(i).isStay = false;
                }
            }
        }
    }

    /** 空席シートの取得 */
    private static int getBlankStartSeatNo(Map<Integer, GroupStatus> seatStatusMap, int time,
            int groupMember) {

        int blankStartSeat = -1; // 空席開始No
        int blankEndSeat = -1; // 空席終了No
        boolean isCount = false; // 空席集計フラグ
        boolean isEnableSeat = false; // 着席可能フラグ

        for (int i = 0; i < 17; i++) {

            if (!seatStatusMap.get(i).isStay) {

                // 座席に客がいない場合

                isCount = true;

                if (blankStartSeat == -1) {

                    blankStartSeat = i;
                }

                blankEndSeat = i;

                if ((blankEndSeat - (blankStartSeat - 1)) >= groupMember) {

                    isEnableSeat = true;
                    break;
                }

            } else {

                // 座席に客がいる場合

                if (isCount) {

                    blankEndSeat = i;

                    if (((blankEndSeat - 1) - blankStartSeat) >= groupMember) {

                        isEnableSeat = true;
                        break;
                    }

                    // 初期化
                    blankStartSeat = -1;
                    blankEndSeat = -1;
                    isCount = false;
                }
            }
        }

        if (isEnableSeat) {

            return blankStartSeat;
        }

        return -1;
    }

    /** 座席に先頭グループ追加 */
    private static int addSeat(Map<Integer, GroupStatus> seatStatusMap, GroupStatus queueStatus,
            int baseTime, int blankStartSeatNo, int n) {

        int groupNo = queueStatus.groupNo;
        int groupMember = queueStatus.groupMember;

        for (int seatNo = blankStartSeatNo; seatNo < (blankStartSeatNo + groupMember); seatNo++) {

            seatStatusMap.get(seatNo).groupNo = groupNo;
            seatStatusMap.get(seatNo).groupMember = groupMember;
            seatStatusMap.get(seatNo).startStayTime = baseTime;
            seatStatusMap.get(seatNo).endStayTime = baseTime;
            seatStatusMap.get(seatNo).isStay = true;
        }

        if (groupNo == n) {

            System.out.println(queueStatus.endStayTime - queueStatus.startStayTime);
        }

        return groupNo;
    }

    static class GroupStatus {

        public int groupNo;
        public int groupMember;
        public int startStayTime;
        public int endStayTime;
        public boolean isStay;

        public GroupStatus() {

        }

        public GroupStatus(int groupNo, int groupMember, int startStayTime, int endStayTime,
                boolean isStay) {
            this.groupNo = groupNo;
            this.groupMember = groupMember;
            this.startStayTime = startStayTime;
            this.endStayTime = endStayTime;
            this.isStay = isStay;
        }
    }
}