PKU 2151 Check the difficulty of problems

http://poj.org/problem?id=2151
tチームが参加し、m個の問題があるプログラミングコンテストを開く。
この時に、あるチームiが、ある問題jを正解する確率はPijとする。

以下の条件を両方とも満たす確率を求めろというような問題。
・全てのチームが1問以上解く。
・コンテストの優勝チームが最低でもn問以上解く。

多段dp的な問題。
諦めようかと思いましたが、解けたことがすごく嬉しかったので、考えたことを書いてみたいと思います。

まず、あるチームがm問中k問解く確率を求めます。
これはdp[出現した問題数][解いた問題数]という感じで、dpします。
これは
dp[j+1][k]=(問題j+1が解ける確率)*dp[j][k-1]+(問題j+1が解けない確率)*dp[j+1][k]
で求められます。

これで個々のチームがk問正解する確率が求められたので、指定された条件を満たす確率もdpで求めます。
これは
dp[参加したチーム数][n問以上といたチームがあるかないか]という添字で確率を記憶しておけば、

dp[j+1][n問以上解いたチームがある]
=dp[j][n問以上解いたチームがない]*(j+1番目のチームがn問以上正解する確率)+
dp[j][n問以上解いたチームがある]*(j+1番目のチームが1問以上正解する確率)

dp[j+1][n問以上解いたチームがない]
=dp[j][n問以上解いたチームがない]*(j+1番目のチームが1以上n問未満の正解数の確率)

という式が立てられるので、あとはそれを適用するだけです。
2時間以上考えました。動的計画法を多段階的に適用するのが面白く感じました。

double dp[40][40];
double ans[1010][2];

double pp[1000];

main(){
  int m,t,n;
  while(cin>>m>>t>>n,m){
    double p0=1,p1=1;
    memset(ans,0,sizeof(ans));
    ans[0][0]=1;

    rep(i,t){
      memset(dp,0,sizeof(dp));
      dp[0][0]=1;
      rep(j,m){
        cin>>pp[j];
        for(int k=0;k<=j+1;k++){
          dp[j+1][k]=(1-pp[j])*dp[j][k];
          if(k)dp[j+1][k]+=pp[j]*dp[j][k-1];
        }
      }
      double tp1n=0,tpn=0;
      for(int j=1;j<n;j++)tp1n+=dp[m][j];
      for(int j=n;j<=m;j++)tpn+=dp[m][j];
      ans[i+1][0]=ans[i][0]*tp1n;
      ans[i+1][1]=ans[i][1]*(1-dp[m][0])+ans[i][0]*tpn;
    }
    printf("%.3f\n",ans[t][1]);
  }
}