CSU_1670_赌神的战斗(概率期望DP)

1670: 赌神的战斗

Time Limit: 5 Sec   Memory Limit: 128 MB
Submit: 20   Solved: 5
[ Submit][ Status][ Web Board]

Description

老千可以有很多,但是赌神,只能有一个!
2015届赌神争霸赛现在在湖南长沙淹鱼塘隆重开幕,2名赌神候选者已经准备就绪。
已知赌神争霸的规则如下:
每个赌神候选者有一个他特制的骰子和特制的战甲,分别能给他提供攻击力和生命值。
两个赌神候选者交手的流程如下:
1,2号赌神候选者分别投掷自己的骰子,点数相同则重新投,否则点数大的赌神候选者会对点数小的造成他们点数差的伤害。如果承受伤害之后生命值小于或等于零,则战败。否则,进行下一轮交手。(投掷出来的点数在 均匀分布)

Input

多组数据,第一行有一个整数 T ,表示有 T 组数据。( T<=100
以下每组数据 每行 个整数 ATK1,HP1 ATK2,HP2 ATK1和HP1 表示 1号赌神 候选者 的骰子最大点数和他的血量,ATK2和HP2 表示 2号赌神 候选者 的骰子最大点数和他的血量
(1<= ATK1 , HP1 , ATK2 , HP2 <= 200 )

Output

两个浮点数(精确到小数点后三位),为1号赌神的胜率和2号赌神的胜率。

Sample Input

2
6 1 6 1
1 24 2 1 

Sample Output

0.500 0.500
0.000 1.000

HINT

Source

LVV



很直接的概率dp

dp[i][j]代表赌神a有i血,赌神b有j血时赌神a的胜率

显然dp[i][0]其中i不等于0的概率应该是1,因为此时赌神a已经赢了

显然dp[o][j]应该是0

这个问题最开始用的是这个很直接的代码

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

const int M=205;
double p[M][M];

int main()
{
    int t;
    int h1,h2;
    int aa1,aa2;
    double a1,a2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&aa1,&h1,&aa2,&h2);
        if(aa1==1&&aa2==1)
        {
            printf("0.000 0.000\n");
            continue;
        }
        if(aa1==1&&aa2!=1)
        {
            printf("0.000 1.000\n");
            continue;
        }
        if(aa2==1&&aa1!=1)
        {
            printf("1.000 0.000\n");
            continue;
        }
        memset(p,0,sizeof(p));
        for(int i=1;i<=h1;i++)
            p[i][0]=1;
        a1=aa1;a2=aa2;
        int tp;
        if(aa1>aa2)
            tp=aa2;
        else
            tp=aa1;
        double pp=aa1*aa2-tp;
        for(int i=1;i<=h1;i++)
        {
            for(int j=1;j<=h2;j++)
            {
                for(int p1=1;p1<=aa1;p1++)            //穷举扔色子的情况
                {
                    for(int p2=1;p2<=aa2;p2++)
                    {
                        if(p1==p2)
                        {
                            tp++;
                            continue;
                        }
                        if(p1>p2)
                        {
                            if(j-p1+p2<=0)
                                p[i][j]+=p[i][0];
                            else
                                p[i][j]+=p[i][j-p1+p2];
                        }
                        if(p1<p2)
                        {
                            if(i-p2+p1>0)
                                p[i][j]+=p[i-p2+p1][j];
                        }
                        //cout<<tp<<endl;
                        //cout<<p1<<" "<<p2<<" "<<p[i][j]<<endl;
                    }
                }
                p[i][j]=p[i][j]/pp;
            }
        }
        printf("%.3lf %.3lf\n",p[h1][h2],1-p[h1][h2]);
    }
    return 0;
}


但是后来数据增强后这个代码不能用了会TLE

原因很简单,其实两个人每次扔完扣多少血的概率是固定的

因此可以初始化一个系数矩阵

这样每次只要针对扣血的情况而不是投色子的情况处理即可

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
 
const int M=205;
double p[M][M];
double pp[M][2];
int main()
{
    int t;
    int h1,h2;
    int aa1,aa2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&aa1,&h1,&aa2,&h2);
        if(aa1==1&&aa2==1)
        {
            printf("0.000 0.000\n");
            continue;
        }
        if(aa1==1&&aa2!=1)
        {
            printf("0.000 1.000\n");
            continue;
        }
        if(aa2==1&&aa1!=1)
        {
            printf("1.000 0.000\n");
            continue;
        }
        memset(p,0,sizeof(p));
        memset(pp,0,sizeof(pp));
        for(int i=1;i<=h1;i++)
            p[i][0]=1;
        int tp,tpma;
        if(aa1>aa2)
        {
            tp=aa2;
            tpma=aa1;
        }
        else
        {
            tp=aa1;
            tpma=aa2;
        }
 
        double ppp=aa1*aa2-tp;
        for(int p1=1;p1<=aa2;p1++)            //这个位置需要用矩阵做优化
        {
            for(int p2=1;p2<=aa1;p2++)     //初始化系数矩阵
            {
                if(p1>p2)
                    pp[p1-p2][0]+=1;
                if(p1<p2)
                    pp[p2-p1][1]+=1;
                //cout<<tp<<endl;
                //cout<<p1<<" "<<p2<<" "<<p[i][j]<<endl;
            }
        }
 
        for(int i=1;i<=h1;i++)
        {
            for(int j=1;j<=h2;j++)
            {
                for(int k=1;k<=tpma;k++)
                {
                    if(k<=i)
                        p[i][j]+=pp[k][0]*p[i-k][j];
 
                    if(k<=j)
                        p[i][j]+=pp[k][1]*p[i][j-k];
                    else
                        p[i][j]+=pp[k][1]*p[i][0];
                }
                p[i][j]=p[i][j]/ppp;
            }
        }
        printf("%.3lf %.3lf\n",p[h1][h2],1-p[h1][h2]);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值