HDU 5816 Hearthstone 概率dp
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5816
Hearthstone
Memory Limit: 65536/65536 K (Java/Others)
输入
The first line is the number of test cases T (T<=10).
Then come three positive integers P (P<=1000), N and M (N+M<=20), representing the enemy’s HP, the number of A-Cards and the number of B-Cards in the card deck, respectively. Next line come M integers representing X (0<X<=1000) values for the B-Cards.
输出
For each test case, output the probability as a reduced fraction (i.e., the greatest common divisor of the numerator and denominator is 1). If the answer is zero (one), you should output 0/1 (1/1) instead.
样例
sample input
2
3 1 2
1 2
3 5 10
1 1 1 1 1 1 1 1 1 1sample output
1/3
46/273
题意
对手的血量为p,你有奥术牌n张,火球牌m张,一开始你可以任抽一张牌,之后如果抽到奥术,你就还可以抽两张牌,如果抽到火球,你就能给对手ai点伤害。
问最后打死对手的概率。
题解
我们可以先考虑从n+m张牌中恰好抽出k张奥术的概率,然后再去考虑用k+1张火球打死对手的概率。
队友用O(1<<(n+m))的复杂度强行模拟了牌堆取数的情况。orzorzorz
代码
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=(a);i<(b);i++)
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;
const int INF=1e9;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
//start----------------------------------------------------------------------
const int maxm=22;
const int maxn=22;
int hp,n,m;
int arr[maxn];
LL sumao[maxn];
LL sumhuo[maxn];
LL one[(1<<maxn)+10];
LL cntao,cnthuo[maxn];
LL gcd(LL a,LL b) {
return b==0?a:gcd(b,a%b);
}
void init() {
cntao=0;
clr(cnthuo,0);
clr(sumao,0);
clr(sumhuo,0);
}
struct Fenshu {
LL a,b;
Fenshu(LL a,LL b):a(a),b(b) {}
Fenshu() {
a=0;
b=1;
}
};
Fenshu add(Fenshu fs1,Fenshu fs2) {
LL ta=fs1.a*fs2.b+fs1.b*fs2.a;
LL tb=fs1.b*fs2.b;
LL g=gcd(ta,tb);
return Fenshu(ta/g,tb/g);
}
Fenshu mul(Fenshu fs1,Fenshu fs2) {
LL ta=fs1.a*fs2.a;
LL tb=fs1.b*fs2.b;
LL g=gcd(ta,tb);
return Fenshu(ta/g,tb/g);
}
void pre() {
clr(one,0);
rep(i,0,(1<<(maxm))) {
rep(j,0,maxm) {
if(i&(1<<j)) one[i]++;
}
}
}
int main() {
pre();
int tc;
scanf("%d",&tc);
while(tc--) {
init();
scanf("%d%d%d",&hp,&n,&m);
rep(i,0,m) scanf("%d",arr+i);
//从n+m张牌中恰好能取出k张奥术的总数,存在sumao[k]里面。
rep(i,0,1<<(n+m)) {
if(one[i]!=n) continue;
cntao++;
int cnt=1,sum=0;
for(int j=0; cnt&&j<(n+m); j++) {
cnt--;
if(i&(1<<j)) {
sum++;
cnt+=2;
}
}
sumao[sum]++;
}
//从m张火球中取出k张并且打死对手的总数,存在sumhuo[k]里面。
rep(i,0,1<<m) {
int cnt=0,sum=0;
rep(j,0,m) {
if(i&(1<<j)) {
cnt++;
sum+=arr[j];
}
}
cnthuo[cnt]++;
if(sum>=hp) {
sumhuo[cnt]++;
}
}
Fenshu ans;
rep(i,0,n+1) {
//i张奥术能让我们抽i+1张火球。
Fenshu tmp;
if(i<m) tmp=mul(Fenshu(sumao[i],cntao),Fenshu(sumhuo[i+1],cnthuo[i+1]));
//最多只能抽到m张火球
else tmp=mul(Fenshu(sumao[i],cntao),Fenshu(sumhuo[m],cnthuo[m]));
ans=add(ans,tmp);
}
printf("%lld/%lld\n",ans.a,ans.b);
}
return 0;
}
//end-----------------------------------------------------------------------