2018 安徽省赛试题 G 然后打五
描述
《炉石传说》是一款考验技(shen)术(chou)的电子游戏,即使你没有玩过这个游戏也没有关系。《炉石传说》里有一张萨满卡牌叫做连环爆裂,它的效果是等概率的造成3-6点伤害。而游戏获胜的条件是将对方英雄的血量全部打掉。例如对方英雄还有5点生命值,你有1张连环爆裂,那么你获胜的概率为0.5。在本题中你无需考虑法力值消耗。
输入
第一行是一个整数T,代表测试数据的组数。接下来的T行每行有2个整数n,h,n代表你一共可以释放的连环爆裂的个数,h代表敌方英雄的血量。其中。
输出格式
共T行,每行是一个小数,保留到小数点后6位。
样例输入
1
1 5
样例输出
0.500000
首先看到这题想到的是数学中的重集的排列组合问题,对于每个元素都有选择和不选择的两种状态,而且还是需要考虑到各个元素相加起来大于血量的概率,时间复杂度会达到O(2^n)甚至更高。所以可以考虑优化这个问题的求解过程,利用递推关系,组合子问题的解求解这个问题。因为一张牌可以等概率的打掉对方3,4,5,6 滴血,所以设f(n,h)表示n张连环爆裂,敌方血量为h时的获胜概率,可以得到我方获胜的概率的递推的关系式:
f(n,h) = (f(n-1,h-3)+f(n-1,h-4)+f(n-1,h-5)+f(n-1,h-6))*1/4;
具体解法:
首先,可以根据上面的递推关系式,写成递归函数。因为当h < 3n时,表示就算每张卡牌都只会打掉对方3点血,我方获胜的概率为1;当h > 6n时,表示就算每张卡牌都能打掉对方6点血,我方获胜的概率也为0;还有当h和n都为0时,设获胜的概率为1;这些作为递归函数的出口。但是在上述递归的过程中,会出现子问题重叠的问题。例如求解f(2,5)时,会去求f(1,2),f(1,3),f(1,4),f(1,5)
,然后求f(1,3)的过程中会去求解f(0,0),求f(1,4)的过程中也会去求解f(0,0)等等。所以可以用动态规划的方法去解决这个问题。
因为f(n,h) = (f(n-1,h-3)+f(n-1,h-4)+f(n-1,h-5)+f(n-1,h-6))*1/4,用动态规划方法时,因为每次记录只会和上次相关,所以只需要记录本次和上一次的记录,即为记述次和偶数次的记录。所以可以开dp[2][30000]用于记录记述次和偶数次获胜的概率。
/*
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
double f(int n, int h)
{
if (h > 6 * n)
return 0;
else if (h == 0 && n == 0)
return 1;
else if (h < 3 * n)
return 1;
else
return (f(n - 1, h - 3) + f(n - 1, h - 4) + f(n - 1, h - 5) + f(n - 1, h - 6))*0.25;
}
int main()
{
int m;
cin >> m;
for (int i = 1; i <= m; i++)
{
int n, h;
cin >> n >> h;
cout << fixed << setprecision(6) << f(n, h) << endl;
}
system("pause");
return 0;
}
*/
#include <iostream>
#include <iomanip>
#include <memory.h>
using namespace std;
double dp[2][30000];
int main()
{
int t;
cin >>t;
while (t--)
{
int n, h;
cin >> n >> h;
memset(dp, 0, sizeof(dp));
dp[0][h] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= h; j++)
{
for (int k = 3; k <= 6; k++)
{
if (i % 2 == 1)
{
if (j > k)
dp[1][j - k] = dp[1][j - k] + dp[0][j] / 4;
else
dp[1][0] = dp[1][0] + dp[0][j] / 4;
}
else
{
if (j > k)
dp[0][j - k] = dp[0][j - k] + dp[1][j] / 4;
else
dp[0][0] = dp[0][0] + dp[1][j] / 4;
}
}
}
}
if(n % 2 == 0)
cout << fixed << setprecision(6) << dp[0][0];
else
cout << fixed << setprecision(6) << dp[1][0];
}
system("pause");
return 0;
}