1277-Minimal Backgammon

問題概要

小さな一人用双六ゲームを考える.
N+1個のマスがあり,0個目が開始地点,N個目が終了時点とする.
さいころを振り出てきた目だけ駒を進める.ゴールより後に行く状態になったら逆方向に進行する.
2つの特殊なマスがあり,
L:一回休み
B:開始地点に戻る.
とする.
Tターン以内にゴールに辿りつける確率はいくつか.

解法

動的計画法
あるターンにあるマスに居る確率をしらべる.

実装(C++)

#include <algorithm>
#include <vector>
#include <iostream>
#include <iomanip>
using namespace std;

typedef long long int lli;

#define REP(i,x) for(int i=0;i<(int)(x);i++)
#define rep(i,s,x) for(int i=s;i<(int)(x);i++)
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();i++)
#define RREP(i,x) for(int i=(x);i>=0;i--)
#define RFOR(i,c) for(__typeof((c).rbegin())i=(c).rbegin();i!=(c).rend();i++)
double DP[105][105];
int main(){
	int N,T,L,B;
	for(;cin>>N>>T>>L>>B;){
		if(N==0)break;
		int STATE[N+1];
		fill(STATE,STATE+N+1,0);
		REP(i,L){
			int t;
			cin>>t;
			STATE[t]=1;
		}
		REP(j,B){
			int t;
			cin>>t;
			STATE[t]=2;
		}
		REP(i,105)REP(j,105)DP[i][j]=0;
		DP[0][0]=1;
		REP(K,T+1){
			REP(i,N){
				REP(j,6){
					int next=i+j+1;
					if(next>N){
						next=N-next+N;
					}
					if(STATE[next]==2){
						next=0;
					}
					if(STATE[next]==1){
						DP[K+2][next]+=DP[K][i]*1/6.0;
					}else{
						DP[K+1][next]+=DP[K][i]*1/6.0;
					}
				}
			}
		}
		double ans=0;
		REP(i,T+1){
			ans+=DP[i][N];
		}
		cout<<setprecision(10)<<setiosflags(ios::fixed)<<ans<<endl;
	}
}