Programming Challenge -PKU- 2044 Weather Forecast

※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

2044 Weather Forecast

問題

解答方針

探索問題のテクニックが詰まっている良問.

一日目の雲の位置はわかっているので,その状態を根としたDFSを行う.雲を動かせる位置の候補は必ず5ヶ所(南北方向に動かすのが2通り,東西方向が2通り,不動が1通り)なので,1つの状態から5つの子状態が生まれるが,「雲の覆うマスでその日にイベントがある」「7日間連続して雲に覆われないマスがある」のどちらかが成り立つ場合は当然カットする.さらに計算時間短縮のために「すでに探索した状態」をカットすることは必須となる.

さて,DFSの単位となる「状態」をどうやって定義するかであるが,「現在の日付」「現在の雲の位置」「各マスの連続日照日数」さえわかればよい.ここで,連続日照日数は0,4,11,15の四隅だけ覚えておけばよいことに注意.0に雨が降るとき1,4,5にも必ず雨が降るので,0の周辺3マスの連続日照日数が0の連続日照日数を超えることは決してなく,他の隅についても同様だからである.

Javaで解く場合はStackとHashSetを使うことになるが,HashSetを正しく利用するためにはequals,hashCodeを上書きする必要がある.

解答例

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(true){
            int n = sc.nextInt();
            if(n==0) break;
            boolean[][] schedule = new boolean[n][16];
            for(int i=0;i<n;i++){
                for(int j=0;j<16;j++){
                    int s = sc.nextInt();
                    if(s==1) schedule[i][j] = true;
                    else schedule[i][j] = false;
                }
            }
            Solver sol = new Solver(n,schedule);
            System.out.println(sol.solve());
        }
    }
}

class Solver{
    int n;
    boolean[][] schedule;
    Solver(int n, boolean[][] schedule) {
        this.n = n;
        this.schedule = schedule;
    }
    public int solve(){
        
        Stack<State> st = new Stack<State>();
        HashSet<State> h = new HashSet<State>();
        
        // initial state
        State init = new State();
        if(valid(init)){
            if(end(init)){
                return 1;
            }
            else{
                st.push(init);
                h.add(init);
            }
        }
        
        // DFS
        while(!st.empty()){
            State s = st.pop();
            
            for(int i=0;i<5;i++){
                State next = s.move(i);
                if(valid(next)&&!h.contains(next)){
                    if(end(next)){
                        return 1;
                    }
                    else{
                        st.push(next);
                        h.add(next);
                    }
                }
            }
        }
        
        return 0;
    }
    private boolean valid(State s){    
        int d = s.day;
        boolean[] sch = schedule[d];
        int x = s.cloud%3;
        int y = s.cloud/3;
        if(sch[4*y+x]||sch[4*y+x+1]||sch[4*y+x+4]||sch[4*y+x+5]) return false;
        
        for(int i=0;i<4;i++) if(s.dry[i]>=7) return false;
        
        return true;
    }
    private boolean end(State s){
        if(n-1==s.day) return true;
        else return false;
    }
}

class State{
    int day;
    int cloud;
    int dry[];
    State(){
        day = 0;
        cloud = 4;
        dry = new int[4];
        for(int i=0;i<4;i++) dry[i] = 1;
    }
    State(State s){
        this.day = s.day;
        this.cloud = s.cloud;
        this.dry = new int[4];
        for(int i=0;i<4;i++) this.dry[i] = s.dry[i];
    }
    public State move(int dir){
        State ret = new State(this);
        ret.day++;
        
        int x = ret.cloud%3;
        int y = ret.cloud/3;
        // dir=0,1 N-S direction
        if(dir==0) y = (y+1)%3;
        else if(dir==1) y = (y+2)%3;
        // dir=2,3 E-W direction
        else if(dir==2) x = (x+1)%3;
        else if(dir==3) x = (x+2)%3;
        // dir=4 no move
        
        ret.cloud = y*3+x;
        
        for(int i=0;i<4;i++) ret.dry[i]++;
        ret.rain();
        return ret;
    }
    private void rain(){
        if(cloud==0) dry[0] = 0;
        else if(cloud==2) dry[1] = 0;
        else if(cloud==6) dry[2] = 0;
        else if(cloud==8) dry[3] = 0;
    }
    public boolean equals(Object o){
        State s = (State)o;
        if(this.day!=s.day||this.cloud!=s.cloud) return false;
        for(int i=0;i<4;i++) if(this.dry[i]!=s.dry[i]) return false;
        return true;
    }
    public int hashCode(){
        int ret = day<<20+cloud<<16;
        for(int i=0;i<4;i++) ret += dry[i]<<(4*i);
        return ret;
    }
}