YSF(J)H006 忘備録

YSF(J)H006

 参加ありがとうございました。本来は部活のメンバーのみで行う予定でしたが、想定以上の人数が参加してくれて驚きでした。
 今回はBとEのWriterをさせていただきました。

A

文字通り実装してください

cout<<"HelloWorld\nLoveNanashi"<<endl;

とかでいいんじゃないでしょうか。

B

配列でi番目のゲートが次いつ開くか、を保存する。というのが想定解でした。
各車両について、入る時間がゲートが開く時間以降であった場合、開くまでの時間を足していく…といった感じです。
また、配列をPriority_queueなどにすると実装が軽くなります。 計算量は前者の場合はO(TN)、後者の場合は挿入にlogNかかるのでO(TlogN)となります。

C

高々5本なので、全通りを試しても構いません。通り数は5C3より20通りです。
また、bit全探索を行うことにより、楽に実装をすることができます。
計算量は25なので十分に間に合います。

D

これ難しくないですか?きれいな解法がわかりません。
差の通りは高々4通りです。
1つ目から各差を足してって、正しいのが4つになったときに残りの一つがおかしい。それがなかったら最初がおかしいという風に説くことができます。

E

DP[i][j]をi回目の操作でMで割った余りがjであるものの通り数、という風に定義します。
1回目の動作にてDP[0][(1~6)%M]+=1とします。
i回目の動作以降、j(0~M)に対してDP[i][(j*(1~6))%M]+=DP[i-1][j]とします。
N回までの動作が終わったとき、DP[N-1][K]が答えとなります。
また、適宜modをとっていってください。計算量はO(NM)です。

Areas on the Cross-Section Diagramを解いた話

問題

onlinejudge.u-aizu.ac.jp

これが問題です。
f:id:SaSaKi:20200423222157p:plain このような感じですね。最終的にいくつ水がたまるか、という問題になっています。
※この場合だと input:\\///\_/\/\\\\/_/\\///__\\\_\\/_\/_/\
output:35 5 4 2 1 19 9
です(合計、個数、それぞれの水の量)

実装

Stackを使います。 今回は3つのStackを使って実装を行いました。

Stack1:\の箇所
Stack2:水の量
Stack3:Stack2の水の場所に対応する\の箇所
必然的にStack2とStack3の大きさは等しくなります。

それではステップごとに実装の仕方を見ていきましょう。

まずは上下を考えない\.../の場合を考えます。
この場合の水の量は右-左です。
これは、間の_の箇所が右-左-1箇所あり、右、左の斜め分が追加されるので右-左-1+1となります。

f:id:SaSaKi:20200423223508p:plain
即興ペイントで申し訳ないけどこういうことです
なので、\を見つけたらその箇所をStack1に詰めて、/を見つけたらStack1から取り出すことで、対応するものを見つけられて横の面積を求めることができます。
面積が求まったらStack2に面積、Stack3に\の場所を入れていきましょう(上記の場合だと(1,1),(4,5)を詰める)

最難関なのがこれが下のものとつながるかどうか、の判定になります。ここでStack3を使用します。
ここで当たり前のことを確認しますが、文字列を左から右に見ているため、必ず新しく出てくる/は右側にあることがわかります。
また、/がKeyとして面積を求めるので、\/となる水の最深部から順に上に面積を求めていっています。よって、判定する水がつながるとしたら必ずStackの一つ前のものとつながることがわかります。
f:id:SaSaKi:20200423224743p:plain 判定ですが、1つ前との関係は上の図で言う上の2種類の関係しかありえません。
Stackを使用し、/に対応する\を探しているので、下のような関係は存在しないことがわかります。
つながる関係は、左上の関係のみとなります。
1つ下は、Stackの一つ前なので、Stack3の最後に入れたやつを取り出して、そいつが今対応している\よりも小さかったら水はつながることがわかります。
当たり前のこと~から/の判定は必要ありません。
水がつながることがわかったらつなげましょう。
Stack2から1つ取り出し、いま求めている面積とつなげ、Stack3から一つ取り出し、今の/と対応している\を代わりに入れてあげればOKです。
また、問題図の19のように、水位が上がると2つの水とつながる場合があります。Stack3から取り出すものが今対応している\より小さい限り繰り返すWhile文などを組むとよいでしょう。
Stackに詰めている関係により、最初に調べた水はどんどん下に行きます。よって出力はStack2の逆順に出力しましょう。

以下、Javaコードになります

String s=scan.next();
        Deque<Integer>index=new ArrayDeque<Integer>();
        Deque<Integer>size=new ArrayDeque<Integer>();
        Deque<Integer>sizeindex=new ArrayDeque<Integer>();
        for(int i=0;i<s.length();i++) {
            char ch=s.charAt(i);
            if(ch=='\\'){
                index.addFirst(i);
                continue;
            }
            else if(ch=='/'){  
                if(index.size()==0) {}
                else {
                    int p=index.pollFirst();// /に対する\
                    int S=i-p;// \/の面積
                    if(size.size()==0) {
                        size.addFirst(S);
                        sizeindex.addFirst(p);
                    }
                    else {
                        while(true) {//\\//みたいな状態
                            if(size.size()==0)break;
                            int x=sizeindex.pollFirst();
                            if(x>p) {
                                S+=size.pollFirst();
                            }
                            else {
                                sizeindex.addFirst(x);
                                break;
                            }
                        }
                        size.addFirst(S);
                        sizeindex.addFirst(p);
                    }
                }
            }
        }
        if(size.size()==0) {
            System.out.println(0);
            System.out.println(0);
            return;
        }
        int ans[]=new int[size.size()];
        long sum=0;
        int cnt=0;
        while(size.size()>0) {
            int x=size.poll();
            sum+=x;
            ans[cnt]=x;
            cnt++;
        }
        System.out.println(sum);
        System.out.print(ans.length+" ");
        ans=revint(ans);
        printArray(ans);