牛客周赛 Round 30 F.小红叒战小紫【期望dp】

原题链接:https://ac.nowcoder.com/acm/contest/73760/F

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

小红这天又和小紫发起了一场对战。对战的规则如下:
两人各有一些怪兽卡。每回合两人随机的从自己当前存活的怪兽卡中抽取一张发起决斗,战斗力低的怪兽卡死亡。如果两张怪兽卡战斗力相同,则无事发生。
游戏会进行到“如果抽卡,则 100% 的概率无事发生”或者“有一方卡牌被用完”时结束。请你计算小红和小紫游戏进行回合数的期望。

输入描述:

第一行输入两个正整数n,m,分别代表小红和小紫的怪兽卡数量。
第二行输入n个正整数ai​,代表小红的每张怪兽卡战斗力。
第三行输入m个正整数bi​,代表小紫的每张怪兽卡战斗力。
1≤n,m≤50
1≤ai​,bi​≤2

输出描述:

一个整数,代表最终回合数期望对1e9+7取模的值。可以证明,最终的答案一定是个有理数,你只需要输出其对1e9+7取模的结果。
分数取模的定义:假设答案是x/y,那么其对p取模的答案是找到一个整数a满足a∈[0,p−1]且a∗y对p取模等于x。

示例1

输入

1 1
1
1

输出

0

说明

显然第一回合都不需要进行,因为一定会无事发生。

示例2

输入

2 3
2 2
1 1 1

输出

3

说明

一定会进行 3 回合,直到小紫的怪兽全部死亡。

示例3

输入

2 1
1 2
2

输出

2

说明

有 1/2 的概率会进行 1 回合,1/4 的概率会进行 2 回合,1/8 的概率会进行 3 回合……
最终答案是 1/2*1+1/4*2+1/8*3+……
该无穷级数收敛于 2。

解题思路:

经典的期望dp,赛时由于状态转移方程出了一点小差错导致没写出来。题目说了只有俩种战斗力的牌,分别为1和2,2已经是最大的牌了,所以2肯定不会死亡,也就是说小红小紫2的牌的数量肯定是不会变的,所以我们只需要考虑俩人1的牌的数量即可,所以dp状态设计时只需要将俩人1的牌的数量设计进入状态即可,我们还需要特判一种情况,就是当俩人都没有2号牌时,那么期望回合数就是0,直接输出0即可,然后考虑dp处理即可。

期望dp处理过程:

状态定义:

定义f[i][j]表示小红战斗力为1的牌的数量为i,小紫战斗力为1的牌的数量为j时的期望回合数。

初始化:

f[0][0]=0,当俩人都没有战斗力为1的卡牌时,期望回合数为0

状态转移:

  • 当小红抽中1,小紫抽中1,表示无事发生,转移到f[i][j]
  • 当小红抽中2,小紫抽中2,表示无事发生,转移到f[i][j]
  • 当小红抽中1,小紫抽中2,小红1的卡牌数减掉1,转移到f[i-1][j]
  • 当小红抽中2,小紫抽中1,小紫1的卡牌数减掉1,转移到f[i][j-1]

1,2俩种无事发生的概率为p1,第三种情况概率为p2,第四种情况概率为p3

p2=i*inv(i+t[2])   *   tt[2]*inv(j+tt[2])

p3=t[2]*inv(i+t[2])   *   j*inv(j+tt[2])

最后加上的这个1表示要把当前这个回合加上,不管从哪种情况转移过来,当前回合肯定加上

f[i][j]=p1*f[i][j]+p2*f[i-1][j]+p3*f[i]][j-1]+1   p1+p2+p3=1

(1-p1)*f[i][j]=p2*f[i-1][j]+p3*f[i][j-1]+1    1-p1=p2+p3

(p2+p3)*f[i][j]=p2*f[i-1][j]+p3*f[i][j-1]+1

f[i][j]=(p2*f[i-1][j]+p3*f[i][j-1]+1)*inv(p2+p3)

最终答案:

答案为小红有t[1]张战斗力为1的卡牌,小紫有tt[1]张战斗力为1的卡牌时的期望回合数

最终答案就是f[t[1]][tt[1]],t[1]表示小红战斗力为1的卡牌数,tt[1]表示小紫战斗力为1的卡牌数。

时间复杂度:状态数量为O(n*m),转移为O(1),所以时间复杂度为O(n*m),n=50,m=50,这个时间复杂度很低肯定是可以过的。

空间复杂度:dp数组f为二维,所以空间复杂度为O(n*m),所以空间要求非常低,题目给了俩百多M空间,空间是肯定足够的。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N=55,mod=1e9+7;

int n,m;
int t[N],tt[N];  
LL f[N][N];

LL qmi(LL x,LL k)  //利用费马小定理,快速幂求逆元
{
    LL res=1;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return res;
}
LL inv(LL x)  //求逆元
{
    return qmi(x,mod-2);
}
int main()
{
    cin>>n>>m;
    int x;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        t[x]++;     //t[1]记录小红卡牌中1的个数,t[2]记录2的个数
    }
    
    for(int i=0;i<m;i++)
    {
        cin>>x;
        tt[x]++; //tt[1]记录小紫卡牌中1的个数,tt[2]记录2的个数
    }
    
    //小红和小紫都没有战斗力为2的卡牌,期望回合数为0
    if(!t[2] && !tt[2]){
        cout<<0<<endl;
        return 0;
    }
    //初始化,小红和小紫都没有战斗力为1的卡牌数
    f[0][0]=0;
    //dp
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            //p2表示小红抽中1小紫抽中2的概率
            //p3表示小红抽中2小紫抽中1的概率
            LL p2=i*inv(i+t[2])%mod*tt[2]%mod*inv(j+tt[2])%mod;
            LL p3=t[2]*inv(i+t[2])%mod*j%mod*inv(j+tt[2])%mod;
            f[i][j]=(p2*f[i-1][j]%mod+p3*f[i][j-1]%mod+1)%mod*inv(p2+p3)%mod;
        }
    //答案为小红有t[1]张战斗力为1的卡牌,小紫有tt[1]张战斗力为1的卡牌时的期望回合数
    cout<<f[t[1]][tt[1]]<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值