ZOJ 3380 Patchouli's Spell Cards 概率DP

题目大意:

就是现在有m个位置, 每个位置可以放n种不同的元素中的一个, 问在至少有L个位置上的元素是一样的概率.


大致思路:

首先如果考虑有L, L + 1, .... m个位置上是一样的方案数不好考虑, 但是可以从反面考虑, 计算只有1, 2, ... L - 1个位置有相同元素的方案数, 用总方案数n^m减去即可

如果用dp[i][j]表示用前i种元素填了j个位置(不一定是前j个), 且每个元素都没有使用达到L个, 的方案数的话有

dp[i][j] = sigma(dp[i - 1][j - k]*C[m - (j - k)][k]) (0 <= k <= min(m, l))

即用前i个元素填充j个位置的方案数相当于前i - 1个元素填充了j - k个位置, 第i个元素要填充k个位置, 这k个位置可以来自于剩下的m - (j - k)个位置中的其中k个

那么不满足题意的方案总数是 sigma(dp[i][m]) (1 <= i <= n) 用所有可能的排列方案减去即可得到可行的方案数.

初始化dp[0][0] = 1, 其他值为0即可

很明显只有当l > m时才会没有可能的方案

考虑到计算时数据很大, 使用java方便一些


代码如下:

Result  :  Accepted     Memory  :  12914 KB     Time  :  1031 ms

/*
 * Author Gatevin
 * Created Time : 2014/12/22 20:16:07
 * File Name: Sora_Kasugano.java
 */
import java.util.Scanner;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.Map;
import java.util.HashMap;
import java.util.Queue;
import java.util.LinkedList;
import java.io.FileOutputStream;

public class Main{
    public static void initC(BigInteger C[][])
    {
        C[0][0] = BigInteger.ONE;
        for(int i = 1; i <= 100; i++)
        {
            C[i][0] = C[0][0];
            for(int j = 1; j <= i; j++)
                if(j == i) C[i][j] = C[0][0];
                else C[i][j] = C[i - 1][j].add(C[i - 1][j - 1]);
        }
        return;
    }
    public static void initdp(BigInteger dp[][])
    {
        for(int i = 0; i <= 100; i++)
            for(int j = 0; j <= 100; j++)
                dp[i][j] = BigInteger.ZERO;
        return;
    }
    public static void main(String args[]){
        int n, m, l;
        Scanner cin = new Scanner(System.in);
        BigInteger C[][] = new BigInteger[110][110];
        BigInteger dp[][] = new BigInteger[110][110];
        initC(C);
        while(cin.hasNext())
        {
            m = cin.nextInt();
            n = cin.nextInt();
            l = cin.nextInt();
            if(l > m)
            {
                System.out.println("mukyu~");
                continue;
            }
            initdp(dp);
            dp[0][0] = BigInteger.ONE;
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= m; j++)
                    for(int k = 0; k <= Math.min(l - 1, j); k++)
                        dp[i][j] = dp[i][j].add(dp[i - 1][j - k].multiply(C[m - (j - k)][k]));
            BigInteger num = BigInteger.ZERO;
            BigInteger dem = BigInteger.valueOf(n).pow(m);
            for(int i = 1; i <= n; i++)
                num = num.add(dp[i][m]);
            num = dem.subtract(num);
            BigInteger tmp = num.gcd(dem);
            num = num.divide(tmp);
            dem = dem.divide(tmp);
            System.out.println(num + "/" + dem);
        }
        return;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值