2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp
【Problem Description】
我方有\(n\)个人,对方有\(m\)个人,每个人都有一个健康值\(h_i\),有\(d\)次攻击,每次随机从所有人中选\(1\)个人,减少其\(1\)健康值。问将对方所有人都消灭的概率是多少?
【Solution】
方法\(1\):
将所有人的健康值作为一种状态,因为\(h\)最大为\(6\),所以可以将所有人的健康值状态通过\(7\)进制数\(hash\)成一个整数,便于保存以及状态的转移。假设目前的状态\(hash\)值为\(state\),健康值大于\(0\)的人数有\(sz\)个,枚举所有可能选择的人,对于其中一个人,使其健康值减\(1\)后的状态为\(nextstate\),则有\(dp[nextstate]=dp[state]\times \frac{1}{sz}\)。经过\(d\)次之后所有状态中,将对方人数为\(0\)的概率加起来即可。
但是有几个问题:
- 最多会有\(10\)个人,所以可能需要\(7^{10}=3\times 10^8\)来保存状态,存不下,但是可以发现,存储状态的时候,与所有人的健康值的大小顺序无关,所以可以强制保证他们有序。这样只需要\(2\times 10^5\)。
- 转换为\(7\)进制且每个状态都对其排序后,最终无法知道那些人是对方的人。可以在保存的时候,将对方的人的健康值取负,这样排序后对方的人一定排在最前端。转换进制也改变为\(13\)进制,且对于每个人的健康值增加\(6\)的偏移值,保证健康值都是正数。这样最终只要判断一下最前端的人的健康值是否大于\(0\)即可。
方法\(2\):
根据健康值分类,统计每种健康值的人数(己方与对方的人分开统计),然后将\(6\)种健康值对应的人数也通过\(7\)进制数\(hash\)到一个整数上,加上对方的人总共最多需要\(12\)位,且高位存储对方每种健康值的人数,这样可以通过判断\(state\)是否小于等于\(all=(666666)_7\),来确定对方的人是否全被消灭。
然后就是转移,枚举健康值\(i\),每次从健康值为\(i\)的人里选\(1\)个人,将其健康值减\(1\),选择健康值为\(i\)的人的概率为\(\frac{h[i]}{sum}\),其中\(h[i]\)为,健康值为\(i\)的人数,\(sum\)为总人数。所以有\(dp[state]=dp[nextstate]*\frac{h[i]}{sum}\)。
【Code】
//方法1
#include
#include
#include
//方法2
#include
#include
#include
#include
#include
#include