动态规划(Hearthstone,HDU 5816)

来源:互联网 发布:excel2013解密软件 编辑:程序博客网 时间:2024/06/03 18:23

看到N,M都很小,一开始想到了暴力搜索+剪枝可以枚举出能够完成击杀的牌的组合,计算量最多为2^20=1e6,(看到网上用状压来枚举,虽然不带剪枝,但是常数应该更小)。

然后在深入考虑预估函数的剪枝效率时,发现P也很小,好像可以动态规划。

dp[i][j][k]表示对于前i张火球术,恰选j张,恰打出k点伤害的概率,dp完后后缀和一下就可以得到前i张火球术,恰选j张,打出大于等于k点伤害的概率。

这样∑dp[M][j][P]就是能够击杀的概率。

但是这样还不够,因为奥术智慧的数量会影响到你的摸牌。

如果没有奥术智慧,那么你只能打出一张火球术。

如果全都是奥术智慧,那么你打不出任何伤害。

适当数量的奥术智慧才能最大化伤害。

然后发现,摸牌的情况就那么几种。

要么就有牌没法摸了,要么就全模完了。

就是考虑把奥术智慧全部打出去,然后摸牌,直到没有奥术智慧了。

然后把火球术再一次性打出去。

情况:

共摸了1张牌,其中0张奥数智慧,结束。

共摸了3张牌,其中1张奥数智慧,结束。

共摸了5张牌,其中2张奥数智慧,结束。

。。。

共摸了N+M张牌,其中N张奥数智慧,结束。

所以dp[i][j]表示前i张牌有j张奥数智慧的概率。

然后看情况转移就好了。(允许摸牌,有牌可摸,乘以概率)


最后答案就是∑上述各种情况的概率乘以击杀的概率,最后一种情况不要算重了。

或者说就是枚举摸到k张火球术的概率乘以k张火球术能击杀的概率。

或者说就是枚举摸牌结束的情况,就知道有k张火球术,然后乘以k张击杀的概率。


后来发现dp[i][j][k]不好算概率,就决定dp方案数,然后除以组合数。

dp也不用求什么后缀和,直接把伤害大于等于P的方案数累加到伤害等于P的方案数中就好了。


最后情况有三种

1、奥数智慧不够用,全拿到都摸不完。

2、奥数智慧刚刚好,全拿到恰好全摸完。

3、奥数智慧太多了,全拿到打出去也摸不到牌了。


只有第3中情况我们需要额外算全部摸完的情况。


一开始dp[i][j][k]计算概率,写完后发现转移是错的,也没法转移,后来才改成方案数。

然后交上去RE,说是除零,分析后发现是最后枚举的时候没考虑到奥术智慧或者火球术不够多,除以了错误的数据,比如0。

改正后交上去WA,后来全部重新思考了一遍,发现是全摸完的情况没讨论对,所以算重或算漏了。

最后才AC。时间复杂度O(NMP),0ms过了。


希望以后能思考得更具体一点,比如说具体到dp该怎么转移,是否正确,最后答案该怎么计算,或不会重复或遗漏,然后再开始写代码。


代码

#include<stdio.h>#include<algorithm>using namespace std;typedef long long ll;const ll maxc = 25;const ll maxp = 1010;ll gcd(ll a,ll b){    return !b?a:gcd(b,a%b);}struct fs{    ll fz,fm;    fs(ll fz=0,ll fm=1):fz(fz),fm(fm)    {        this->yuefen();    }    void zero()    {        fz=0;        fm=1;    }    void one()    {        fz=1;        fm=1;    }    void yuefen()    {        ll GCD = gcd(fz,fm);        fz/=GCD;        fm/=GCD;    }    fs operator + (const fs& rhs) const    {        fs ret;        ll GCD = gcd(fm,rhs.fm);        ret.fm=fm/GCD*rhs.fm;        ret.fz=rhs.fm/GCD*fz+fm/GCD*rhs.fz;        ret.yuefen();        return ret;    }    fs operator * (const fs& rhs) const    {        fs ret;        ret.fm=fm*rhs.fm;        ret.fz=fz*rhs.fz;        ret.yuefen();        return ret;    }    fs operator - (const fs& rhs) const    {        fs ret;        ll GCD = gcd(fm,rhs.fm);        ret.fm=fm/GCD*rhs.fm;        ret.fz=rhs.fm/GCD*fz-fm/GCD*rhs.fz;        ret.yuefen();        return ret;    }    fs operator / (const fs& rhs) const    {        fs ret;        ret.fm=fm*rhs.fz;        ret.fz=fz*rhs.fm;        ret.yuefen();        return ret;    }    void prll()    {        printf("%lld/%lld\n",fz,fm);    }};ll P,N,M;ll X[maxc];fs num[maxc][maxc];ll kill[maxc][maxc][maxp];ll C[maxc][maxc];void read(){    scanf("%lld %lld %lld",&P,&N,&M);    for(ll i=1;i<=M;i++)        scanf("%lld",X+i);}void getnum(){    for(ll i=0;i<=N+M;i++)        for(ll j=0;j<=i;j++)            num[i][j].zero();    num[0][0].one();    for(ll i=0;i<N+M;i++)        for(ll j=0;j<=min(i,N);j++) if(i==0||(i-1)/2<j)        {            if(j<N)                num[i+1][j+1]=num[i+1][j+1]+num[i][j]*fs(N-j,N+M-i);            if(i-j<M)                num[i+1][j]=num[i+1][j]+num[i][j]*fs(M-i+j,N+M-i);        }}void getkill(){    for(ll i=0;i<=M;i++)        for(ll j=0;j<=i;j++)            for(ll k=0;k<=P;k++)                kill[i][j][k]=0;    kill[0][0][0]=1;    for(ll i=0;i<M;i++)        for(ll j=0;j<=i;j++)            for(ll k=0;k<=P;k++)            {                kill[i+1][j+1][min(P,k+X[i+1])]=kill[i+1][j+1][min(P,k+X[i+1])]+kill[i][j][k];                kill[i+1][j][k]=kill[i+1][j][k]+kill[i][j][k];            }}void getC(){    C[0][0]=1;    for(ll i=1;i<maxc;i++)    {        C[i][0]=1;        for(ll j=1;j<=i;j++)            C[i][j]=C[i-1][j]+C[i-1][j-1];    }}void prllnum(){    puts("----------");    puts("num");    for(ll i=1;i<=N+M;i++)    {        for(ll j=0;j<=min(i,N);j++)            printf("%lld/%lld ",num[i][j].fz,num[i][j].fm);        puts("");    }    puts("----------");}void prllkill(){    puts("----------");    puts("kill");    for(ll i=1;i<=M;i++)    {        printf("i=%lld\n",i);        for(ll j=0;j<=i;j++)        {            printf("j=%lld\n",j);            for(ll k=0;k<=P;k++)                printf("%lld ",kill[i][j][k]);            puts("");        }        puts("");    }    puts("----------");}void prllC(){    for(ll i=0;i<maxc;i++)    {        for(ll j=0;j<=i;j++)            printf("%lld ",C[i][j]);        puts("");    }}void solve(){    read();    getnum();    //prllnum();    getkill();    //prllkill();    fs ans;    for(ll i=1;i<=N+M;i+=2)    {        ll j = i/2;        if(j>N) break;        if(i-j>M) break;        ans=ans+num[i][j]*fs(kill[M][i-j][P],C[M][i-j]);    }    if(N>=M)    {        ll i = N+M;        ll j = N;        ans=ans+num[i][j]*fs(kill[M][M][P],C[M][i-j]);    }    ans.prll();}int main(){    getC();    //prllC();    ll T;    scanf("%lld",&T);    while(T--) solve();    return 0;}


原创粉丝点击