HDU 5816 Hearthstone(概率DP+状压)

Problem Description
Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation and your only hope depends on the top of the card deck, and you draw the only card to solve this dilemma. We call this "Shen Chou Gou" in Chinese.

Now you are asked to calculate the probability to become a "Shen Chou Gou" to kill your enemy in this turn. To simplify this problem, we assume that there are only two kinds of cards, and you don't need to consider the cost of the cards.
-A-Card: If the card deck contains less than two cards, draw all the cards from the card deck; otherwise, draw two cards from the top of the card deck.
-B-Card: Deal X damage to your enemy.

Note that different B-Cards may have different X values.
At the beginning, you have no cards in your hands. Your enemy has P Hit Points (HP). The card deck has N A-Cards and M B-Cards. The card deck has been shuffled randomly. At the beginning of your turn, you draw a card from the top of the card deck. You can use all the cards in your hands until you run out of it. Your task is to calculate the probability that you can win in this turn, i.e., can deal at least P damage to your enemy.




Input
The first line is the number of test cases T (T<=10).
Then come three positive integers P (P<=1000), N and M (N+M<=20), representing the enemy’s HP, the number of A-Cards and the number of B-Cards in the card deck, respectively. Next line come M integers representing X (0<X<=1000) values for the B-Cards.


Output
For each test case, output the probability as a reduced fraction (i.e., the greatest common divisor of the numerator and denominator is 1). If the answer is zero (one), you should output 0/1 (1/1) instead.


Sample Input
2
3 1 2
1 2
3 5 10
1 1 1 1 1 1 1 1 1 1


Sample Output
1/3

46/273


题意:BOSS有P点血。一共有N张A牌,和M张B牌。A牌可以再抽两张,第i张B牌可以对BOSS造成Xi点伤害。第一次可以抽一张,问获胜概率。


思路:先预处理出当拥有K张B牌是获胜的概率,设dp[i][j]表示拥有i张A牌和j张B牌的概率。j<=i+1为合法情况。

抽出1张A牌,1张B牌的概率dp[i-1][j-1]*(n-i+1)*(m-j+1)/C(2, n+m-i-j+2);

抽出2张A牌的概率dp[i-2][j]*C(2, n-i+2)/C(2, n+m-i-j+2);

抽出2张B牌的概率dp[i][j-2]*C(2, m-j+2)/C(2, n+m-i-j+2);


几个要注意的地方:

1. 全场要用long long

2. if(i==n&&j==m&&(n+m)%2==0)  表示此时仅剩下一张B牌可抽,所以要算上dp[i][j-1]

3. 如果j张B牌获胜的概率为1了,就不会再继续抽牌了


#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string.h>
#include <vector>
#include <queue>
#include <stack>
#include <ctype.h>
using namespace std;

typedef long long ll;

ll gcd(ll a,ll b)
{
    if(b == 0)
    {
        return a;
    }
    return gcd(b,a%b);
}

struct node    //分数结构体
{
    ll up,down;
    void init(ll x,ll y)
    {
        up = x;   //分子
        down = y;   //分母
    }
    
    node operator+ (node n2)
    {
        built();
        n2.built();
        ll x = down * n2.down;
        ll y = up * n2.down + down*n2.up;
        ll d = gcd(x,y);
        x/=d;
        y/=d;
        node n3;
        n3.init(y,x);
        return n3;
    }
    
    node operator- (node n2)
    {
        built();
        n2.built();
        ll x = down *n2.down;
        ll y = up * n2.down - down*n2.up;
        ll d = gcd(x,y);
        x/=d;
        y/=d;
        node n3;
        n3.init(y,x);
        return n3;
    }
    
    node operator*(node n2)
    {
        built();
        n2.built();
        ll x = down * n2.down;
        ll y = up*n2.up;
        ll d = gcd(x,y);
        x/=d;
        y/=d;
        node n3;
        n3.init(y,x);
        return n3;
    }
    void built()
    {
        if(down<0)
        {
            down = -down;
            up = -up;
        }
        if(up==0)
        {
            down=1;
        }
    }
    void shownode()
    {
        cout<<up<<"/"<<down<<endl;
    }
};

node dp[25][25];
ll a[25];
node win[25];

ll CAL(ll x)   //组合数
{
    return x*(x-1)/2;
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        ll p,n,m;
        scanf("%lld%lld%lld",&p,&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%lld",&a[i]);
            win[i].up=0;
            win[i].down=0;
        }
        win[m].up=0;
        win[m].down=0;
        ll mmm=1<<m;
        for(int i=0;i<mmm;i++)
        {
            ll res=i;
            ll cnt=0;
            ll cnt1=0;
            ll sum=0;
            while(res>0)
            {
                ll xxx=res%2;
                if(xxx==1)
                {
                    cnt1++;
                    sum+=a[cnt];
                }
                res/=2;
                cnt++;
            }
            if(sum>=p) win[cnt1].up++;
            win[cnt1].down++;
        }
        node ans;
        ans.init(0,1);
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=m;j++)
            {
                dp[i][j].init(0,1);
            }
        }
        dp[1][0].init(n,n+m);
        dp[0][1].init(m,n+m);
        ans=ans+dp[0][1]*win[1];
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=i+1&&j<=m;j++)
            {
                if(j>=1&&i>=j)
                {
                    node xx;
                    xx.up=(n-i+1)*(m-j+1);
                    xx.down=CAL(n+m-i-j+2);
                    dp[i][j]=dp[i][j]+dp[i-1][j-1]*xx;
                }
                if(i>=2&&i-2>=j)
                {
                    if(!(win[j].up==1&&win[j].down==1))
                    {
                        node xx;
                        xx.up=CAL(n-i+2);
                        xx.down=CAL(n+m-i-j+2);
                        dp[i][j]=dp[i][j]+dp[i-2][j]*xx;
                    }
                }
                if(j>=2&&i>=j-2)
                {
                    node xx;
                    xx.up=CAL(m-j+2);
                    xx.down=CAL(n+m-i-j+2);
                    dp[i][j]=dp[i][j]+dp[i][j-2]*xx;
                }
                if(i==n&&j==m&&(n+m)%2==0)
                {
                    if(i>=j-1) dp[i][j]=dp[i][j]+dp[i][j-1];
                }
                if(i+1==j||j==m)
                {
                    ans=ans+dp[i][j]*win[j];
                }
            }
        }
        ans.shownode();
    }
    return 0;
}

/*
 10
 41 7 13
 334 500 169 724 478 358 962 464 705 145 281 827 961
 491 15 5
 942 827 436 391 604
 902 13 7
 292 382 421 716 718 895 447
 726 11 9
 538 869 912 667 299 35 894 703 811
 322 13 7
 673 664 141 711 253 868 547
 644 2 18
 757 37 859 723 741 529 778 316 35 190 842 288 106 40 942 264 648 446
 805 10 10
 729 370 350 6 101 393 548 629 623 84
 954 16 4
 840 966 376 931
 308 4 16
 439 626 323 537 538 118 82 929 541 833 115 639 658 704 930 977
 306 13 7
 386 21 745 924 72 270 829
 */
/*
 1/1
 9/10
 143/228
 211/285
 9/10
 111077/232560
 134735/325584
 17/20
 193/228
 2881/3420
*/

最后是数据,可供参考。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值