HDU 5816 Hearthstone 概率dp

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5816

Hearthstone

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
#### 问题描述 > Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation and your only hope depends on the top of the card deck, and you draw the only card to solve this dilemma. We call this "Shen Chou Gou" in Chinese. > Now you are asked to calculate the probability to become a "Shen Chou Gou" to kill your enemy in this turn. To simplify this problem, we assume that there are only two kinds of cards, and you don't need to consider the cost of the cards. > > - A-Card: If the card deck contains less than two cards, draw all the cards from the card deck; otherwise, draw two cards from the top of the card deck. > - B-Card: Deal X damage to your enemy. > > Note that different B-Cards may have different X values. > At the beginning, you have no cards in your hands. Your enemy has P Hit Points (HP). The card deck has N A-Cards and M B-Cards. The card deck has been shuffled randomly. At the beginning of your turn, you draw a card from the top of the card deck. You can use all the cards in your hands until you run out of it. Your task is to calculate the probability that you can win in this turn, i.e., can deal at least P damage to your enemy.

输入

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 1

sample 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-----------------------------------------------------------------------
posted @ 2016-08-11 23:47  fenicnn  阅读(159)  评论(0编辑  收藏  举报