susunshunのお粗末な記録

お粗末に丁寧に生きる

0503: Cup - ArrayListとLinkedList

ちょっとずつ問題が難しくなってきましたね。

問題

長いのでリンクをご参照ください。
Cup | Aizu Online Judge

イメージこんな

スポーツスタッキング - YouTube

方針

今回は綺麗な解法が思いつかず結構悩んだ。。。
一旦寝て次の日電車でぼーっとしてるときににいくつがルールがあることに気づく

【ルール①】移動したコップは次のターンには元いた方向に動かさない
☆      ☆
◎*  → ◎*
ABC   ABC

☆をA→Bに移動しました、それだけです。
☆は次に動くとしたらCです、Aにはいきません、元に戻っちゃうので

【ルール②】トレイAからトレイBに連続でコップは動かせない
①で☆をA→Bに動かしました。

AB間に限定して考えた場合、
・動かしたコップ(☆)は次はそのまま ※ルール①
・動かしたコップ(☆)の隣にあるコップ(◎)は直前まで動かしたコップ(◎)の下にあった。つまり問題の(規則2)により動かしたコップ方向には移動できない

(規則 2)大きいコップの上に小さいコップを重ねてはいけない.

B→Aパターンも考えてみましょう
 ☆    ☆ 
◎*  → ◎*
ABC   ABC

☆:そのまま(ルール①)
◎:動かせない(ルール②)
*:動かせつとしたらC方向

【ルール③】移動はAB間↔BC間の繰り返し
①②を踏まえるトAB間で移動を行った場合、BC間で移動を行うしかありません。
BC間で移動を行った場合、AB間で移動を行うしかありません。

これに気づいてしまえばあとは簡単。

アルゴリズム

①AB間で大きいコップを小さいコップ方向に移動
②BC間で大きいコップを小さいコップ方向に移動
③”①②”の繰り返し

回答

package joaapp;

import java.io.*;
import java.util.Stack;

class Main{
    static int n,m,ans=0;
    
    static Stack<Integer> stackA_ini;
    static Stack<Integer> stackB_ini;
    static Stack<Integer> stackC_ini;
    
    static Stack<Integer> stackA;
    static Stack<Integer> stackB;
    static Stack<Integer> stackC;
    
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    
    public static void main(String[] a)throws IOException{
        while(true){
            inputData();
            ans = Math.min(calcFromLeft(),calcFromRight());
            ans = (ans <= m) ? ans : -1;               
            System.out.println(ans);
        }
        
    }
    
    static int calcFromLeft(){
        int step = 0;
        
        //左初動
        while(true){
            // 終了判定
            if(isFinished()) break;
 
            // 左2つのコップで入れ替え
            if(peekStack(stackA)>peekStack(stackB))
                stackB.push(stackA.pop());
            else
                stackA.push(stackB.pop());
            
            step++;
            
            // 終了判定
            if(isFinished()) break;
                
            // 右2つのコップで入れ替え
            if(peekStack(stackB)>peekStack(stackC))
                stackC.push(stackB.pop());
            else
                stackB.push(stackC.pop());
            
            step++;
        }
        return step;
    }
    
    static int calcFromRight(){
        
        int step = 0;
        stackA = stackA_ini;
        stackB = stackB_ini;
        stackC = stackC_ini;
                 
        //右初動
        while(true){     
            // 終了判定
            if(isFinished()) break;
            
            // 右2つのコップで入れ替え
            if(peekStack(stackB)>peekStack(stackC))
                stackC.push(stackB.pop());
            else
                stackB.push(stackC.pop());

            step++;
            
            // 終了判定
            if(isFinished()) break;          
            
            // 左2つのコップで入れ替え
            if(peekStack(stackA)>peekStack(stackB))
                stackB.push(stackA.pop());
            else
                stackA.push(stackB.pop());
            
            step++;
        }
        return step;
    }
      
    static boolean isFinished(){
        if(stackB.empty() && (stackA.empty() || stackC.empty()))
            return true;
        else
            return false;
    }
    
    static int peekStack(Stack<Integer> stack){
        return stack.empty() ? 0:stack.peek();      
    }
    
    static void inputData() throws IOException{
        
        String[] nm = in.readLine().split(" ");
        String[] cupList;
        int cupNum = 0;

        stackA = new Stack<Integer>();
        stackB = new Stack<Integer>();
        stackC = new Stack<Integer>();
        stackA_ini = new Stack<Integer>();
        stackB_ini = new Stack<Integer>();
        stackC_ini = new Stack<Integer>();
        
        n = Integer.parseInt(nm[0]);
        m = Integer.parseInt(nm[1]);
        
        if(n+m == 0){
            System.exit(0);
        }
        
        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackA_ini.push(Integer.parseInt(cupList[j+1]));
            stackA.push(Integer.parseInt(cupList[j+1]));
        }

        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackB_ini.push(Integer.parseInt(cupList[j+1]));
            stackB.push(Integer.parseInt(cupList[j+1]));
        }
        
        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackC_ini.push(Integer.parseInt(cupList[j+1]));
            stackC.push(Integer.parseInt(cupList[j+1]));
        }
    }
}

できました。これを提出すると。。。※susunshunが僕です
f:id:susunshun:20150120234941p:plain

11人中11位。。。結構いい線いってると思ったのに。

ArrayListとLinkedList

こんな記事を見かけました。
JavaのArrayListとLinkedListの違い

LinkedListは情報処理試験でお馴染みのスタックやキューを実装しているクラスです。
今回スタックが実装をイメージしやすかったのstackクラスを使用しました。

上記記事によると

先端の要素・終端の要素に対して操作できるメソッドを持っているので

とありますが今回はコップは最大でも15個。先端終端に簡単にアクセスできるというメリットを享受しにくい気がします。

だったら一般的に処理が軽いArrayListで実装してみたらどうだ。

import java.io.*;
import java.util.ArrayList;

class Main{
    static int n,m,ans=0;
    
    static ArrayList<Integer> stackA_ini ;    
    static ArrayList<Integer> stackB_ini ;    
    static ArrayList<Integer> stackC_ini ;    
    
    static ArrayList<Integer> stackA;
    static ArrayList<Integer> stackB;
    static ArrayList<Integer> stackC;    
    
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    
    public static void main(String[] a)throws IOException{
        while(true){
            inputData();
            ans = Math.min(calcFromLeft(),calcFromRight());
            ans = (ans <= m) ? ans : -1;               
            System.out.println(ans);
        }
    }
    
    static int calcFromLeft(){
        int step = 0;
        
        //左初動
        while(true){
            // 終了判定
            if(isFinished()) break;
 
            // 左2つのコップで入れ替え
            if(peekStack(stackA)>peekStack(stackB)){
                stackB.add(stackA.get(stackA.size()-1));
                stackA.remove(stackA.size()-1);
            }else{
                stackA.add(stackB.get(stackB.size()-1));
                stackB.remove(stackB.size()-1);
            }
            
            step++;
            
            // 終了判定
            if(isFinished()) break;
                
            // 右2つのコップで入れ替え
            if(peekStack(stackB)>peekStack(stackC)){
                stackC.add(stackB.get(stackB.size()-1));
                stackB.remove(stackB.size()-1);
            }else{
                stackB.add(stackC.get(stackC.size()-1));
                stackC.remove(stackC.size()-1);
            }
            step++;
        }
        return step;
    }
    
    static int calcFromRight(){
        
        int step = 0;
        stackA = stackA_ini;
        stackB = stackB_ini;
        stackC = stackC_ini;
                 
        //右初動
        while(true){     
            // 終了判定
            if(isFinished()) break;
            
            // 右2つのコップで入れ替え
            if(peekStack(stackB)>peekStack(stackC)){
                stackC.add(stackB.get(stackB.size()-1));
                stackB.remove(stackB.size()-1);
            }else{
                stackB.add(stackC.get(stackC.size()-1));
                stackC.remove(stackC.size()-1);
            }
            step++;
            
            // 終了判定
            if(isFinished()) break;          
            
            // 左2つのコップで入れ替え
            if(peekStack(stackA)>peekStack(stackB)){
                stackB.add(stackA.get(stackA.size()-1));
                stackA.remove(stackA.size()-1);
            }else{
                stackA.add(stackB.get(stackB.size()-1));
                stackB.remove(stackB.size()-1);
            }
            
            step++;
        }
        return step;
    }
      
    static boolean isFinished(){
        if(stackB.isEmpty() && (stackA.isEmpty() || stackC.isEmpty()))
            return true;
        else
            return false;
    }
    
    static int peekStack(ArrayList<Integer> stack){
        return stack.isEmpty() ? 0:stack.get(stack.size()-1);      
    }
    
    static void inputData() throws IOException{
        
        String[] nm = in.readLine().split(" ");
        String[] cupList;
        int cupNum = 0;
        
        stackA = new ArrayList<Integer>();
        stackB = new ArrayList<Integer>();
        stackC = new ArrayList<Integer>();
        stackA_ini = new ArrayList<Integer>();
        stackB_ini = new ArrayList<Integer>();
        stackC_ini = new ArrayList<Integer>();        
        
        n = Integer.parseInt(nm[0]);
        m = Integer.parseInt(nm[1]);
        
        if(n+m == 0){
            System.exit(0);
        }
        
        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackA_ini.add(Integer.parseInt(cupList[j+1]));
            stackA.add(Integer.parseInt(cupList[j+1]));
        }

        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackB_ini.add(Integer.parseInt(cupList[j+1]));
            stackB.add(Integer.parseInt(cupList[j+1]));
        }
        
        cupList = in.readLine().split(" ");
        for(int j=0; Integer.parseInt(cupList[0])>j; j++){
            stackC_ini.add(Integer.parseInt(cupList[j+1]));
            stackC.add(Integer.parseInt(cupList[j+1]));
        }
    }
}

スタックを使ったときと全く処理は同じです。違いはArrayListかstackかだけ。
では実行時間を見てみましょう。
f:id:susunshun:20150120235814p:plain

!?!?

性能が大幅に改善されました。
アルゴリズムだけでなく使用するクラスの選択でここまで変わるものなのかぁ、一つ学びになりました。