HDU 5816 Hearthstone (状压dp+概率)

这题它的难点不在状压dp的表示或者转移上,感觉它是难在如何想到这个概率该用什么表示。

即求:

伤害≥p的牌排列数/所有牌的全排列.

对于求概率或期望的题,我想的是找分子分母是什么,但是每次都是一脸懵逼,不知道怎么下手。包括上次的ATM存款那题,完全不知道分子分母该是什么。实际上这题的表示应该比ATM那题简单,关键是要读清楚题目的意思:在本回合能胜利的概率。意思是这回合赢没赢都结束了,不会再继续抽牌。所以就是一种状态,这个状态下B牌能不能把敌人打死,这样的话想到也就不难了。

状压的话没什么好说的,老套路

【代码】

/* ***********************************************
Author        :angon

************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%i64d%I64d",&n,&m)
#define mst(a,k)  memset(a,k,sizeof(a))
#define LL long long
#define N 1005
#define mod 1000000007
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}
int t,m,n,p;
int a[22];
LL dp[(1<<20)+10],f[22];
//前0~n-1是A牌,n~m+n-1是B牌;
//dp[i] 代表有多少种方式可以抽到i这种状态的牌
int count_1(int x)
{
    int cnt=0;
    while(x)
    {
        x&=(x-1);
        cnt++;
    }
    return cnt;
}

int judge(int x)
{
    int ret=0;
    for(int i=0;i<m;i++)
    {
        if((x>>i)&1)
            ret+=a[i];
    }
    return ret>=p;
}

int judge_continue(int x)
{
    int A=0,B=0;
    for(int i=0;i<m;i++)
        if(1&(x>>i))
            B++;
    for(int i=m;i<n+m;i++)
        if(1&(x>>i))
            A++;
    return A+1-B>0;
}

LL gcd(LL a,LL b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scan(t);
    f[0]=f[1]=1;
    for(int i=2;i<=20;i++) f[i]=f[i-1]*i;
    while(t--)
    {
        scan(p);scann(n,m);
        int tot=m+n;
        REP(i,0,m) scan(a[i]);
        mst(dp,0);              
        dp[0]=1;
        LL ans=0;
        for(int i=0;i<(1<<tot);i++)
        {
            if(!dp[i]) continue;  //如果这种牌不可能被抽到
            if(judge(i))   //伤害达到了p,没必要再抽了
            {
                ans+=dp[i]*f[tot-count_1(i)];
                continue;
            }
            if(!judge_continue(i)) continue; //不能抽牌了
            for(int j=0;j<tot;j++)  //转移
            {
                if((i&(1<<j))==0)
                {
                    dp[i|(1<<j)] += dp[i];
                }
            }
        }
        LL k = gcd(ans,f[tot]);
        printf("%I64d/%I64d\n",ans/k,f[tot]/k);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值