实验1动态规划——小明打王者

实验1动态规划——小明打王者

问题:

小明想要在王者荣耀游戏里晋升一个段位,假设他一共需打了n场比赛,且必须成功赢得至少70%的场次才能成功晋升。假设每场比赛小明获胜的概率分别为p1,p2,…,pn,请帮他算出成功晋级段位的概率是多少?

输入:
参数1:整数num(0<=num<=1000),表示比赛的场数。参数2:整数数组p[num] = {p1,p2,…,pnum},其中pi表示小明有pi%的概率赢得第i场比赛。(0<=pi<=100)

输出:
成功晋级段位的概率,保留小数点后5位,最后结果四舍五入。

分析:

——分析最优子结构:首先确定描述原问题以及其子问题的统一结构。设a[i][j]表示小明已经进行了i场比赛并且赢了j场的概率,那么问题转化为求a[num][0.7*num](其中0.7*num向上取整)一直加到a[num][num]的结果。对于a[i][j]而言,由于其仅有一个精确解而非一个优化问题,因此将其解视为最优解,a[i][j]的解取决于其子结构(子问题)的解:若第i场输了,那么前i场赢j场的概率等于前i-1场赢j场的概率乘以第i场输的概率;若第i场赢了,那么前i场赢j场的概率等于前i-1场赢j-1场的概率乘以第i场赢的概率。可以说,父问题有(最优)解时,子问题也有(最优)解,即具有最优子结构性质。

——确定递推关系:根据最优子结构,可以得出递推公式如下:a[i][j] = a[i-1][0] * (1 - P[i]) , j=0 ; a[i][j] = a[i-1][j] * (1-P[i]) + a[i-1][j-1] * P[i] , j>0;

——计算最优值:初始化最小子问题:a[0][0] = 1,a[k][k] = p1p2…pk;根据递推公式,画出求解矩阵a的顺序:

1
TBD1P1
TBD2TBDnum+1
TBD3
TBD4
TBDnump1p2…pnum

首先根据题意,i>=j,则矩阵只需求解对角线及左下半部分。初始化矩阵的对角线。根据递推公式,计算第一列的所有元素需要其上方的元素,计算其它列的元素需要其上方和左上方的元素,因此按照图中的TBD标号顺序逐列求解即可。

——求最优解:由于本题只需给出晋级概率,故不需额外空间来记录问题轨迹并traceback

伪代码:

for i = 1 to num do
    a[i][i] = a[i - 1][i - 1] * 0.01d * p[i - 1]

for j = 0 to num - 1 do
    for i = j + 1 to num do
        if j = 0
            a[i][0] = a[i + j - 1][0] * (1 - 0.01d * p[i - 1])
        else
        a[i][j] = a[i - 1][j] * (1 - 0.01d * p[i - 1]) + a[i - 1][j - 1] * 0.01d * p[i - 1]

for i = ceil(0.7 * num) to num
    pass += a[num][i]

代码:

public class GamePassProbability {

    // 计算小明晋级成功的可能性
    // 输入:int数组p表示每一局的获胜可能性,int数字num表示一共要打多少局
    // 输出:小明晋级的可能性
    public static double calculatePassProbability(int[] p, int num) {
        double pass = 0.0d;// 最终结果
        double[][] a = new double[num + 1][num + 1];// 子问题记录矩阵
        // 初始化矩阵,对角线赋值
        a[0][0] = 1.0d;
        for (int i = 1; i <= num; i++) {
            a[i][i] = a[i - 1][i - 1] * 0.01d * p[i - 1];
        }

        // 依次计算矩阵内容,按照列顺序自上而下计算
        for (int j = 0; j <= num - 1; j++) {// 先枚举列
            for (int i = j+1; i <= num; i++) {// 再枚举行
                if (j == 0)// 第一列
                    a[i][0] = a[i + j - 1][0] * (1 - 0.01d * p[i - 1]);
                else {// 其他列
                    a[i][j] = a[i - 1][j] * (1 - 0.01d * p[i - 1]) + a[i - 1][j - 1] * 0.01d * p[i - 1];
                }
            }
        }

        a[0][0] = 1.0d;


        // 得出结果
        int s = (int) Math.ceil(0.7 * num);// 向上取整
        for (int i = s; i <= num; i++)
            pass += a[num][i];
        return pass;
    }

    public static void main(String[] args) {
        int[] p1 = { 50, 50, 50, 50 };
        int[] p2 = { 80, 80, 90, 90, 99 };
        int[] p3 = {};
        double result1 = calculatePassProbability(p1, 4);
        double result2 = calculatePassProbability(p2, 5);
        double result3 = calculatePassProbability(p3, 0);
        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }

}

复杂度分析:

——输入规模大小:序列p长度num,此处记为n

——基本操作次数:根据递推式计算a[i][j]

——输入情况:只与问题的规模有关,和问题的输入好坏无关

——时间复杂度:算法是非递归的,直接累加求和就可以得到其时间复杂度为O(n2)

——空间复杂度:额外空间为O(n2)

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值