文系プログラマーのプログラミング備忘録

Java、競プロ、数学などについて書いてます

UNOシミュレータ [ yukicoder No.769 ]

yukicoder.me

問題

UNOの対戦ログから局面をシミュレーションして、


・勝った人の番号
・勝った人が最初に持っていた手札の枚数


を出力せよ。


考察

純粋な実装の問題です。
よって、コードを見ていただいたほうが手っ取り早いです。


コード

void solve (FastScanner in, PrintWriter out) {
	
	int n = in.nextInt(), m = in.nextInt();
	String[] turn = in.nextArray(m);
	
	int next = 0; //次は誰の番か
	int[] put = new int[n]; //i番目の人がカードを何枚置いたか
	int[] draw = new int[n]; //i番目の人がカードを何枚引いたか
	boolean reverse = false; //順番の方向(reverseが置かれるたびに変化)
	int draw2Num = 0; //drawtwoが場に何枚蓄積されているか
	int draw4Num = 0; //drawfourが場に何枚蓄積されているか
	
	for (int i=0; i<m; i++) {
		
		//next番目の人がカードを置く
		put[next]++;
		
		//最後の人がカードを置いた場合、そこで終了なのでループを抜ける
		if (i == m-1) break;

		if (turn[i].equals("drawtwo")) {
			
			draw2Num += 2; //場へ蓄積させる
			
			//次の人が最後の人でなく、かつ、置いた札が"drawtwo"でない場合、
			//その人の一つ前の人に蓄積した"drawtwo"を引かせる
			if (i!=m-1 && !turn[i+1].equals("drawtwo")) {
				
				//次の人へターンを回す関数
				next = nextTurn(next, 1, n, reverse);
				
				//蓄積した"drawtwo"を引く
				draw[next] += draw2Num;
				draw2Num = 0;
				
			}
			
			//もう1回ターンを回す(補足にて後述)
			next = nextTurn(next, 1, n, reverse);
			
		}
		else if (turn[i].equals("drawfour")) { //"drawtwo"と同じ
			draw4Num += 4;
			if (i!=m-1 && !turn[i+1].equals("drawfour")) {
				next = nextTurn(next, 1, n, reverse);
				draw[next] += draw4Num;
				draw4Num = 0;
			}
			next = nextTurn(next, 1, n, reverse);
		}
		else if (turn[i].equals("skip")) {
			next = nextTurn(next, 2, n, reverse); //移動量を2にする
		}
		else if (turn[i].equals("reverse")) {
			reverse = reverse==true? false : true; //方向を逆転
			next = nextTurn(next, 1, n, reverse);
		}
		else { //通常のターン移行
			next = nextTurn(next, 1, n, reverse);
		}
	}

	//<出力>
	//最後に置いた人は、その時点で手札が0枚のはず
	//最初に持っていた枚数 - 置いた枚数 + 引いた枚数 = 0
		
	//最初に持っていた枚数 = 置いた枚数 - 引いた枚数
	out.println((next+1)+" "+(put[next]-draw[next]));

}



static int nextTurn (int now, int move, int num, boolean b) {
	if (b == true) {
		return Math.max(now-move, (now-move+num)%num);
	}
	else {
		return Math.min(now+move, (now+move)%num);
	}
}

補足

この問題で一番難しいのは "drawtwo","drawfour" の処理です。
"drawtwo" の処理を例にとって説明します。


ログに


・drawtwo
・number


とあった場合、


・i 番目の人が "drawtwo" を置く
・i+1 番目の人が "number" を置く


ではなく、


・i 番目の人が "drawtwo" を置く
・i+1 番目の人がカードを2枚引く
・i+2 番目の人が "number" を置く


と処理しなければなりません。ログは2つですが、実際には3人のプレイヤーが関与しています。
ではここで、上記のコードの "drawtwo" の処理部分を見てみます。


if (turn[i].equals("drawtwo")) {
	
	//i番目の人の処理
	draw2Num += 2;
	
	//i+1番目の人の処理
	if (i!=m-1 && !turn[i+1].equals("drawtwo")) {
		next = nextTurn(next, 1, n, reverse);
		draw[next] += draw2Num;
		draw2Num = 0;
	}
	
	//i+2番目の人にターンを回す処理
	next = nextTurn(next, 1, n, reverse);
	
}


他のカードより余計にターンが回されていたのは、ログには現れないプレイヤーがいたせいなんですね。