HDU 5816 Hearthstone(状压DP)

原创 2016年08月30日 19:36:01

Description
有一个卡组,有两种卡牌,A牌n张,B牌m张,抽到一张A牌可以从卡组中再抽两张,抽到一张B牌可以给敌人造成一定的伤害,现在告诉敌人的血量P以及m张B牌的伤害值,初始状态可以从卡组中拿一张牌,问有多大概率可以打败敌人(即抽到牌的总伤害值大于等于敌人的血量)
Input
第一行一整数T表示用例组数,每组用例首先输入三个整数p,n,m分别表示敌人血量,A牌数,B牌数,之后m个整数ai表示第i张B牌的伤害值
(T<=10,p<=1000,n+m<=20,0< ai<=1000)
Output
对于每组用例,输出打败敌人的概率(输出最简分数形式)
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
Solution
因为最多20张牌,所以想到用一个20位二进制数表示一个状态,用dp[i]表示拿到牌的状态为i是否合法,初始化就是dp[1<< i]=1,i=0,1,…,n+m-1,对于一个状态i,如果已经拿到x张A牌和y张B牌,统计这y张B牌的总伤害,如果大于等于p则后面的牌随意排列都可以打败敌人,那么对答案的贡献就是dp[i]*(n+m-x-y)!,之后通过判断这个状态是否可以继续拿牌来转移到下一个状态,x张A牌可以拿2x张牌,去掉初始手中的一张牌,故若2x-x-y+1>0说明该状态可以接着拿牌,进而枚举这个状态中0的位赋为1即转移到一下状态
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn ((1<<20)+11)
ll f[maxn];
int cnt[maxn];
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
void init()
{
    f[0]=1;
    for(int i=1;i<=20;i++)f[i]=1ll*i*f[i-1];
    for(int i=0;i<(1<<20);i++)
    {
        cnt[i]=0;
        for(int j=0;j<20;j++)
            if(i&(1<<j))cnt[i]++;
    }
}
int T,p,n,m,v[22];
ll dp[maxn];
bool check(int x)
{
    int ans=0;
    for(int i=n;i<n+m;i++)
        if(x&(1<<i))ans+=v[i-n];
    if(ans>=p)return 1;
    return 0;
}
int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&p,&n,&m);
        for(int i=0;i<m;i++)scanf("%d",&v[i]);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n+m;i++)dp[1<<i]=1;
        ll ans=0,N=1<<(n+m);
        for(int i=0;i<N;i++)
        {
            int tot=cnt[i],x=cnt[i&((1<<n)-1)],y=tot-x;
            if(check(i))
            {
                ans+=dp[i]*f[n+m-tot];
                continue;
            }
            if(x-y+1<=0)continue;
            for(int j=0;j<n+m;j++)
                if(!(i&(1<<j)))dp[i|(1<<j)]+=dp[i];
        }
        ll g=gcd(ans,f[n+m]);
        printf("%I64d/%I64d\n",ans/g,f[n+m]/g);
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

hdu5816Hearthstone(状压DP)

Hearthstone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) To...

(HDU 5816)2016 Multi-University Training Contest 7 Hearthstone (状压DP、搜索)

思路能够赢的状态有: 1. 抽一张B就赢 2. 抽一定数量的A、配合BTip: 1. 如果造成的伤害已经≥p\geq p,那么就直接乘上剩下的全排列即可 2. 从已经抽了的牌数可以推出能够再抽...

HDU 5816 Hearthstone (状压dp)

Hearthstone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tota...

【HDU5816】Hearthstone(状压DP)

推荐几篇好看懂的博客。 参考博客: http://blog.csdn.net/qq_21057881/article/details/52167285 http://blog.csdn.net/...

hdu5816 多校7 Hearthstone【组合计数+dp】

题目链接:Hearthstone题目大意: 牌堆有n张奥术牌,奥术牌可以再从牌堆摸两张牌, m张伤害牌,伤害各为xix_i,初始从牌堆摸一张,问能不能本回合击杀给定hp的对手。(n+m= hp且k-...
  • lhfl911
  • lhfl911
  • 2016年08月09日 21:17
  • 454

hdu5816 Hearthstone 状态压缩dp

/* 题目描述:在牌堆中有n+m(n+m
  • jijijix
  • jijijix
  • 2017年01月21日 23:07
  • 87

hdu5816 状压dp

题意:有两种牌,A:可以从牌堆摸两张牌,B:可以对敌人造成x点伤害。牌堆有n张A和m张B,一开始摸一张牌,敌人有p点血,问把一个回合把敌人打死的概率 思路:因为n+m 对于状态i,状态转移方程为 nu...

2016多校训练Contest7: 1008 Hearthstone hdu5816

Problem Description Hearthstone is an online collectible card game from Blizzard Entertainment. S...
  • lqybzx
  • lqybzx
  • 2016年08月09日 21:35
  • 298

HDU5816(2016多校第七场)——Hearthstone(暴力枚举,状态压缩)

Hearthstone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) To...

HDU 5816 Hearthstone

题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=5816题目大意: 你在玩炉石,对手n滴血,你每次抽的卡有两种类型,一种类型是再抽两张,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HDU 5816 Hearthstone(状压DP)
举报原因:
原因补充:

(最多只允许输入30个字)