题目链接:https://codeforc.es/gym/101933/problem/E
题意:玩家和敌手进行游戏,玩家有n枚棋子,敌手有m枚棋子,每枚棋子剩余血量均给出,现一共出现d次攻击,从所有棋子中随机挑选一个攻击,攻击值为1,若棋子血量为0则消失,问敌手全部棋子均消失的概率
思路:题目数据量很小,可以dfs,但是如果直接暴力搜会t,需要记忆化,但是要把所有状态存下来,如果开一个11维的数组空间上没办法,看了别人的思路之后发现,可以巧妙的压缩这些状态,用一个15位的整数st表示当前状态,从高位到低位的含义:
第1~3位表示当前剩余攻击次数,第4~9位分别表示敌方1~6滴血的棋子数,第10~15位表示玩家1~6滴血的棋子数,可见当该整数减去高位的攻击次数后若小于1e7则敌方所有棋子均已消失
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
int p[2][7];
int n, m, d;
ll c[20];
map<ll, double> mm;
double dfs(ll sta, int t)
{
if (mm.count(sta))return mm[sta];
if (sta - t*c[13] < c[7])return 1.0;
if (t <= 0)return 0;
int res = n + m - p[0][0] - p[1][0];
double ans = 0;
for (int i = 0; i < 2; i++)
{
for (int j = 6; j > 0; j--)
{
if (p[i][j])
{
p[i][j]--, p[i][j - 1]++;
double tmp;
ll stan;
if(j==1)
stan = sta - c[i * 6 + j] - c[13];
else
stan = sta - c[i * 6 + j] + c[i * 6 + j - 1] - c[13];
tmp = dfs(stan, t - 1);
mm[stan] = tmp;
p[i][j]++, p[i][j - 1]--;
ans += tmp * p[i][j] / (1.0*res);
}
}
}
return ans;
}
int main()
{
scanf("%d%d%d", &n, &m, &d);
memset(p, 0, sizeof(p));
int a = 0, b = 0;
c[1] = 1;
for (int i = 2; i < 15; i++)
c[i] = c[i - 1] * 10;
ll stanow = d * c[13];
for (int i = 0; i < n; i++)
{
int k;
scanf("%d", &k);
a += k;
p[0][k]++;
stanow += c[k];
}
for (int i = 0; i < m; i++)
{
int k;
scanf("%d", &k);
a += k;
b += k;
p[1][k]++;
stanow += c[6+k];
}
if (a <= d)
{
printf("1\n");
}
else if (b > d)
{
printf("0\n");
}
else
{
double ans = dfs(stanow, d);
printf("%.8lf\n", ans);
}
}