hdu 5816 Hearthstone (状压dp)

比赛的时候没想出来..

题意:给p,n,m三个数,分别表示敌人的血量,可抽A牌的数量(A牌可以再抽两张),可抽B牌的数量(B牌对敌人造成a[i]点伤害),问现在轮到我抽一张牌(抽了A牌后又可以抽2张,如此往复),问最后打死敌人的概率。



样例:

第一组:5/15(只能从15张牌里先取1张A牌才能获胜) 

第二组:[(5/15) * (10/14) * (4/13)]  + [(5/15) * (4/14) * (10/13)] + [(5/15) * (4/14) * (3/13)]  



思路:上面样例是直观计算的,用程序不太好跑,仔细想一下:

其实就是问:(打死敌人的方案数 / 总的取牌方案数),n+m<=20可以考虑用状态压缩枚举出所有取牌的情况,看了别人的博客,这题只要求出一个伤害h>=p当前状态就可以不向下递推了,可以用N-A-B(这里代表取牌的数量)求全排列。


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;
ll dp[1 << 21];
ll f[21];
int a[21];

void init(){
    f[0] = 1;
    for(int i = 1;i<=20;i++) f[i] = f[i-1] * (i * 1LL);
}
int main()
{
    init();

    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,p;
        scanf("%d%d%d",&p,&n,&m);
        for(int i =0;i<m;i++)
            scanf("%d",&a[i]);
        int N = n + m;//[0,m)代表B卡的数量,[m,N)表示A卡数量
        memset(dp,0,sizeof dp);dp[0] = 1;
        for(int s = 0;s< (1 << N);s++){
            if(dp[s]!=0){
                int A =0,B =0,h=0;//A,B牌数和伤害点数
                for(int i = 0;i< m;i++){
                    if((s >> i )& 1){
                        B++;h+=a[i];
                    }
                }
                if(h>=p) continue;//已经可以打死对手,不需要拿牌了
                for(int i = m;i<N;i++)
                    if((s>>i)&1) A++;
                if(A-B +1>0){
                    for(int i =0;i<N;i++)
                        if(!(s>>i&1)) dp[s^(1<<i)]+=dp[s]; 
                }

            }
        }
        ll up = 0,down = f[N];
        
        for(int s = 0;s<(1<<N);s++){
            if(dp[s]!=0){
                int A =0,B =0,h=0;
                //枚举当前拿牌方案中A,B牌的数量
                for(int i = 0;i<m;i++)
                    if(s >> i & 1) {B++;h+=a[i];}
                for(int i = m;i<N;i++)
                    if(s >> i & 1) A++;
                if(h>=p){
                    up+=dp[s] * f[N-A-B];
                }
            }
        }
        ll g = __gcd(up,down);
        printf("%I64d/%I64d\n",up/g,down/g);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值