【概率DP】 ZOJ 3380 Patchouli's Spell Cards
题意:有m个位置,每个位置填入一个数,数的范围是1~n,问至少有L个位置的数一样的概率
思路:
总数是n^m,我们求没有L个位置一样的数的概率
* 设 dp[i][j]表示用前i个数,填充j个位置的方案数(要符合没有L个位置是一样的数)
* dp[i][j]=dp[i-1][j]+Sigm( dp[i-1][j-k]*C[m-(j-k)][k] ) k<=j&&k<L
* 其实就是看第i个数,可以不填,填一个位置,两个位置······这样累加过来。
* 那么最后的答案就是 (n^m-dp[1~n][m])/(n^m)
代码:
import java.util.*; import java.io.*; import java.math.*; public class Main { static BigInteger[][] dp=new BigInteger[110][110]; static BigInteger[][] C=new BigInteger[110][110];//组合数 public static void main(String arg[]) { Scanner cin=new Scanner(new BufferedInputStream(System.in)); for(int i=0;i<105;i++) { C[i][0]=C[i][i]=BigInteger.ONE; for(int j=1;j<i;j++) C[i][j]=C[i-1][j-1].add(C[i-1][j]); } int N,M,L; while(cin.hasNext()) { M=cin.nextInt(); N=cin.nextInt(); L=cin.nextInt(); BigInteger tol=BigInteger.valueOf(N).pow(M); if(L>M) { System.out.println("mukyu~"); continue; } if(L>M/2)//这个时候可以直接用组合数求出来 { BigInteger ans=BigInteger.ZERO; for(int i=L;i<=M;i++) ans=ans.add(C[M][i].multiply(BigInteger.valueOf(N-1).pow(M-i))); ans=ans.multiply(BigInteger.valueOf(N)); BigInteger gcd=ans.gcd(tol); System.out.println(ans.divide(gcd)+"/"+tol.divide(gcd)); continue; } for(int i=0;i<=N;i++) for(int j=0;j<=M;j++) { dp[i][j]=BigInteger.ZERO; } dp[0][0]=BigInteger.ONE; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { for(int k=0;k<L&&k<=j;k++) dp[i][j]=dp[i][j].add(dp[i-1][j-k].multiply(C[M-(j-k)][k])); } BigInteger ans=BigInteger.ZERO; for(int i=1;i<=N;i++) ans=ans.add(dp[i][M]); ans=tol.subtract(ans); BigInteger gcd=ans.gcd(tol); System.out.println(ans.divide(gcd)+"/"+tol.divide(gcd)); } } }