2018-2019 ACM-ICPC Nordic Collegiate Programming Contest E题 Explosion Exploit (状压+记忆化dfs)

题目链接: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
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long ll;
int p[2][7];
int n, m, d;
ll c[20];
map 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);
	}
}

 

你可能感兴趣的:(记忆化搜索)