HDU 5816 Hearthstone (状压dp)

来源:互联网 发布:mac 可以当修容的眼影 编辑:程序博客网 时间:2024/06/06 15:43

题目大概说有两种卡牌,使用A牌能从牌堆摸两张牌,使用B牌能对对方造成xi点伤害。在你的回合,你从牌堆摸一张牌,问能对对方造成p点及以上伤害的概率。

要求的其实就是能造成p点以上伤害的牌堆排列数/牌堆全排列数

全排列而且总数为20,这种就该想到尝试用状压DP。。

  • dp[S]表示已经摸的牌的集合为S的可行排列方案数

对于一个状态S,我们能从这个集合中已经摸的牌知道还能摸几张,即A的数目 * 2 - A的数目 - B的数目 + 一开始能摸的一张牌

考虑状态的转移,我是用我为人人实现的:从小到大枚举状态S,判断S是否合法,即S的方案数不为0且还能摸牌,然后通过S去更新S+i(i∉S)状态的值。另外如果S能造成的伤害已经大于等于p了,那就没必要去更新它能转移到的状态,因为还剩下的牌直接求全排列计算其贡献,这样也能避免重复计算。

最后就循环遍历各个合法的状态累加贡献,这个贡献就是dp值 * 还没摸的牌的全排列数。这样就求出能造成p点以上伤害的牌堆排列数,再和全排列数用GCD搞搞输出答案即可。

没想到排列,老是纠结于概率,然后就g了。。

#include <set>#include <map>#include <stack>#include <queue>#include <deque>#include <cmath>#include <vector>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define L(i) i<<1#define R(i) i<<1|1#define INF  0x3f3f3f3f#define pi acos(-1.0)#define eps 1e-9#define maxn 1100010#define MOD 1000000007int n,m;long long p,a[22];long long dp[maxn],fac[22];int cnt1,cnt2;int ok(int x){    cnt1 = 0,cnt2 = 0;    for(int i = 0; i < n; i++)        if(x & (1<<i))            cnt1++;    for(int i = 0; i < m; i++)        if(x & (1<<(n+i)))            cnt2++;    return cnt1 - cnt2 + 1 >= 0;}long long get_sum(int x){    long long sum = 0;    for(int i = 0; i < m; i++)        if(x&(1<<(n+i)))            sum += a[i];    return sum;}long long gcd(long long a,long long b){    if(b == 0)        return a;    return gcd(b,a%b);}int main(){    int t,C = 1;    scanf("%d",&t);    fac[0] = 1;    for(int i = 1; i < 22; i++)        fac[i] = fac[i-1] * i;    while(t--)    {        scanf("%lld%d%d",&p,&n,&m);        memset(dp,0,sizeof(dp));        long long ans = 0;        for(int i = 0; i < m; i++)            scanf("%lld",&a[i]);        dp[0] = 1;        for(int i = 0; i < 1<<(n+m); i++)        {            if(!dp[i] || !ok(i))                continue;            if(get_sum(i) >= p)            {                ans += dp[i] * fac[n+m-cnt1-cnt2];                continue;            }            if(cnt1-cnt2+1 == 0)                continue;            for(int j = 0; j < n+m; j++)                if(!(i&(1<<j)))                    dp[i|(1<<j)] += dp[i];        }        long long k = gcd(ans,fac[n+m]);        printf("%lld/%lld\n",ans/k,fac[n+m]/k);    }    return 0;}


0 0
原创粉丝点击