Javaで解いてみた!! Paiza練習問題「長テーブルのうなぎ屋」


はじめに

この記事は、プログラミング初心者が書いています。
間違っていること、もっとこうした方がいいよ!!等ございましたら、
コメントお願いします。

また、この問題はpaizaの練習問題になります。
ランクの上下には全く関係のないものですので、ご安心ください。

問題

今、m個のグループの人達が座席に順番に座りに来ます。i番目(1≦i≦m)のグループの人数をa_i人とします。
彼らは、長テーブルに並んだ座席の内、ある連続するa_i個の座席に一斉に座ろうとします。

ただしお客さんは江戸っ子なので、それら座席のうち、いずれか一つでも既に先客に座られている座席があった場合、
一人も座らずにグループ全員で怒って帰ってしまいます。江戸っ子は気が早いんでぃ。

入力では、i番目のグループが座ろうとする連続した座席の位置は、整数b_iにより指定されます。
i番目のグループは、座席番号b_iの座席を始点として、そこから時計回りにa_i個分の座席に座ろうとします。

最後のグループが座りに来た後、無事に長テーブルの座席に着席出来ている人数を出力するプログラムを作成してください。

入力される値

入力はm+1行から成ります。
1行目にはn(座席数)とm(グループ数)が半角スペース区切りで入力されます。
i+1行目(1≦i≦m)には2個の整数a_i(グループの人数)とb_i(着席開始座席番号)が半角スペース区切りで入力されます。

入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。

例)
6 3
3 2
1 6
2 5

期待する出力

最後のグループが座りに来た後、無事に座席に着席出来ている人数を1行で出力してください。

例)

条件

1≦n≦100
1≦m≦100
1≦a_i≦n
1≦b_i≦n

100点を取ったコード

package paiza;
import java.util.*;

public class unagiya {

    public static void main(String[] args) {
        /*
         * 店の総席数、来店するグループ数をString型で取得しlineに格納
         * lineに格納したデータをsplitで分けてString配列のlineArrayに格納
         */
        Scanner scanner = new Scanner(System.in);
        String line = scanner.nextLine();
        String[] lineArray = line.split(" ");
        /*
         * lineArrayに格納したデータをキャストして各変数に格納
         * 
         * seatNum:座席の数
         * totalGuest:来店するグループの数
        */
        int seatNum = castString(lineArray[0]);
        int totalGuest = castString(lineArray[1]);

        /*
         * seats:座席数をhashMapで作成
         * Integerが席番号
         * Booleanが人の有無(trueなら座れる,)
         */
        Map<Integer,Boolean> seats = new HashMap<>();
        for(int i = 1; i <= seatNum; i++){
            seats.put(i,true);
        }
        /*
         *isSitCustomer:客が選んだ席に人がいるかいないか確認
         *いなければtrueを投げる
         *
         * choiceSeat:来た客が選ぶ基点となる席番号
         *menberNum:来た客の数
         */

        int choiceSeat = 0;
        int memberNum = 0;

        for(int i = 0; i < totalGuest; i++){
            //コンソールの入力値を受け取り、
            //memberNumとchoiceSeatに代入
            line = scanner.nextLine();
            lineArray = line.split(" ");

            memberNum = castString(lineArray[0]);
            choiceSeat = castString(lineArray[1]);

            //isSitGuestがtrueなら、客の選んだ席、人数の座席情報をfalseに書き換える
            if(isSitGuest(seats,choiceSeat,memberNum)){
                for(int j = 0,index = 1 ; j < memberNum; j++){
                    if(seats.size() < choiceSeat + j){
                        seats.put(index, false);
                        index++;
                    } else {
                        seats.put(choiceSeat + j,false);
                    }
                }
            }
        }

        //座席情報のfalseの数を調べてリザルトに代入
        int result = 0;

        for(int i = 1; i <= seats.size(); i++){
            if(seats.get(i).equals(false)){
                result++;
            }
        }
        System.out.println(result);
    }

    //String型の数値をint型にキャスト
    public static int castString(String strNum){
        int num = Integer.parseInt(strNum);
        return num;
    }

    /*
     * Map座席の情報、客が選んだ席、客の人数を受け取り、
     * 客が選んだ席から、客の人数分、falseがあるかチェック
     * 
     * 条件
     * 客の人数が席より多ければfalseを返す
     * 座席情報に一つでもfalseがあればfalseを返す
     */
    public static boolean isSitGuest(Map seats, int choiceSeat, int menberNum){
        if(menberNum > seats.size()){
            return false;    
        }

        boolean flag = true;

        for(int i = 0,index = 1; i < menberNum; i++){

            //index:座席情報の一番最初の1を指定
            //客の数が座席番号を超えたら1から数える時に使用

            if(seats.size() < choiceSeat + i){
                if(seats.get(index).equals(false)){
                    flag = false;
                    index++;
                } else {
                    index++;
                }
            } else if(seats.get(choiceSeat + i).equals(false)){
                flag = false;
            }

            }
        if(flag){
            return true;
        } else {
            return false;
        }
    }
}

結果

解説

まず大雑把にフローチャートを書いてみました

これで大雑把にですが、どんなコードを書けばいいかわかりましたね。

この問題の難しいところは、座席が円になっていることでしょうか。
座席が10あって、4人組のお客さんが8番目の席から座りたい!!
ってことなら、
8、9、10、1
と座席番号を跨ぐ処理をしなくてはならないことです。

小一時間考えた結果、for分のカウンタiとは別にカウンタを作ればいいんじゃね?
ということでやってみたのがこの部分

for(int j = 0,index = 1 ; j < memberNum; j++){
        if(seats.size() < choiceSeat + j){
            seats.put(index, false);
            index++;
        } else {
            seats.put(choiceSeat + j,false);
        }
}

もっといいやり方があるんじゃないかなあとも思ってます。

自分で自分のコードをレビュー

まず、座席情報にfalseを書き込む部分のネストが深すぎる。
思いつかないけど、もっと簡潔に書けそうです。

また、席を跨ぐ部分は、同じ処理を何度もしているので、
メソッドにしちゃえばいいじゃんとも思いますが、
一度問題を解いてしまってから脳みそが仕事しませんねww

余談

まず一番最初に思ったこと

日本語って難しい・・・


それっぽいものが出来るが動かず1日・・・。
なんとか動いたが、問題を正しく認識するのに1日・・・。
最後の1日で、なんとか動くものが書けました。


2日目に出来たものが、要件をほとんど汲んでないんですよね。
座席を26個作ったりして・・・。
相手が何を望んでいるかを正しく認識しないと、作り直しという地獄を見ますねこれ。


また、名著「リーダブルコード」先生曰く、
「コメントを書くくらいなら、一瞬でわかるコードを書け」
とのことですが、今回はJavaを書き慣れてない方にもわかるよう
簡単なコメントを記述しています。

意見、感想、改善点、僕の悪口、募集中です

初めての投稿でしたが、今回はこれで終わりです。
Qiitaの暗黙の了解などがあればおそらく全てガン無視して書いてますが、
読んだ方は何か記録を残していただけるとありがたいです。

最後まで読んでくれた方へ
ありがとうございました。

追記

練習問題に関しては、paiza様から投稿可能との連絡をいただきました。

参考

Paiza

オブジェクト指向編

Javaで解いてみた!! Paiza練習問題「長テーブルのうなぎ屋」 〜オブジェクト指向編〜