TopCoder SRM 395 Div1 Medium Skyscrapers
問題
N個のビルが一列に並んでいて、それぞれの高さは1,2,3,...,Nである。
ビルiが左側から見えるとは、すべてのjiなるjについてh[j]
制約条件
N≦100
方針
メモ化再帰。
まずは最も高いビルの位置を決める。
これがi番目にあるとき、1〜i-1番目に見えるビルがleftSide-1個あり、i+1〜N番目に見えるビルがrightSide-1個ある。
左側にあるビルの選び方はC(N-1,i-1)通りである。
n個のビルのうち、k個のビルが見えているような並び方をf(n,k)とおくと、
この中で一番高いビルがi番目に来るとして、
f(n,k)=ΣC(n-1,i-1)*f(i-1,k-1)*(n-i)!
と表せるため、同じ問題のサイズが小さいものに帰着し、再帰により解ける。
二項係数C(n,k)はあらかじめ計算しておく。
ソースコード
ll P[101][101], C[101][101]; ll dp[101][101]; ll solve(int n,int k){ if(n==k)return 1; if(n<k||k==0)return 0; ll &res=dp[n][k]; if(res>=0)return res; res=0; for(int i=k;i<=n;i++) (res+=solve(i-1,k-1)*C[n-1][i-1]%mod*P[n-i][n-i]%mod)%=mod; return res; } class Skyscrapers { public: int buildingCount(int N, int leftSide, int rightSide) { memset(dp,-1,sizeof(dp)); rep(i,101)rep(j,i+1)C[i][j]=i==0||j==i?1:(C[i-1][j-1]+C[i-1][j])%mod; rep(i,101)rep(j,101)P[i][j]=j?P[i][j-1]*(i-j+1)%mod:1; ll ans=0; for(int i=1;i<=N;i++) (ans+=C[N-1][i-1]*solve(i-1,leftSide-1)%mod*solve(N-i,rightSide-1)%mod)%=mod; return ans; } };