PKU 1037-A decorative fence
問題概要
長さが1〜Nの木がそれぞれあり、それを使って柵を作りたい。
ただし、柵はジグザグな形になるようにするようにしたい。
C番目の作り方を求めよ。
解法
dp[進行方向の残りの木の本数][全体の残りの木の本数]とすることで、O(N^2)でパターン数を求めることが出来る。
パターン数から作り方を復元する。
実装(C++)
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> using namespace std; typedef long long lli; int N; lli C; bool input(){ static int testcase=-1; if(testcase<0){ cin>>testcase; } if(testcase==0)return false; cin>>N>>C; C--; testcase--; return true; } lli dp[30][30]; // forward:進行方向の数、n;全体の残りの数) lli solve(int forward,int n){ if(dp[forward][n]>=0)return dp[forward][n]; if(n==0)return 1; lli sum=0; for(int i=0;i<forward;i++){ sum+=solve(i+n-forward,n-1); } return dp[forward][n]=sum; } int main(){ memset(dp,-1,sizeof(dp)); while(input()){ int s=0,b; for(int i=1;i<=N;i++){ //最初の数を決める if(C<solve(i-1,N-1)){ b=-1; s=i; break; }else{ C-=solve(i-1,N-1); } if(C<solve(N-i,N-1)){ b=1; s=i; break; }else{ C-=solve(N-i,N-1); } } bool used[21]; memset(used,0,sizeof(used)); used[s]=true; cout<<s; int back=s; for(int i=1;i<N;i++){ if(b==1){ int cnt=0; for(int j=1;j<back;j++){ if(used[j]==false)cnt++; } for(int j=back+1;j<=N;j++){ if(used[j])continue; if(C<solve(cnt,N-i-1)){ cout<<" "<<j; back=j; used[j]=true; break; }else{ C-=solve(cnt,N-i-1); } cnt++; } }else{ int cnt=0; for(int j=1;j<=N;j++){ if(used[j]==false)cnt++; } for(int j=1;j<back;j++){ if(used[j])continue; cnt--; if(C<solve(cnt,N-i-1)){ cout<<" "<<j; back=j; used[j]=true; break; }else{ C-=solve(cnt,N-i-1); } } } b=b*-1; } cout<<endl; } }