这题它的难点不在状压dp的表示或者转移上,感觉它是难在如何想到这个概率该用什么表示。
即求:
伤害≥p的牌排列数/所有牌的全排列.
对于求概率或期望的题,我想的是找分子分母是什么,但是每次都是一脸懵逼,不知道怎么下手。包括上次的ATM存款那题,完全不知道分子分母该是什么。实际上这题的表示应该比ATM那题简单,关键是要读清楚题目的意思:在本回合能胜利的概率。意思是这回合赢没赢都结束了,不会再继续抽牌。所以就是一种状态,这个状态下B牌能不能把敌人打死,这样的话想到也就不难了。
状压的话没什么好说的,老套路
【代码】
/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%i64d%I64d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a))
#define LL long long
#define N 1005
#define mod 1000000007
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}
int t,m,n,p;
int a[22];
LL dp[(1<<20)+10],f[22];
//前0~n-1是A牌,n~m+n-1是B牌;
//dp[i] 代表有多少种方式可以抽到i这种状态的牌
int count_1(int x)
{
int cnt=0;
while(x)
{
x&=(x-1);
cnt++;
}
return cnt;
}
int judge(int x)
{
int ret=0;
for(int i=0;i<m;i++)
{
if((x>>i)&1)
ret+=a[i];
}
return ret>=p;
}
int judge_continue(int x)
{
int A=0,B=0;
for(int i=0;i<m;i++)
if(1&(x>>i))
B++;
for(int i=m;i<n+m;i++)
if(1&(x>>i))
A++;
return A+1-B>0;
}
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scan(t);
f[0]=f[1]=1;
for(int i=2;i<=20;i++) f[i]=f[i-1]*i;
while(t--)
{
scan(p);scann(n,m);
int tot=m+n;
REP(i,0,m) scan(a[i]);
mst(dp,0);
dp[0]=1;
LL ans=0;
for(int i=0;i<(1<<tot);i++)
{
if(!dp[i]) continue; //如果这种牌不可能被抽到
if(judge(i)) //伤害达到了p,没必要再抽了
{
ans+=dp[i]*f[tot-count_1(i)];
continue;
}
if(!judge_continue(i)) continue; //不能抽牌了
for(int j=0;j<tot;j++) //转移
{
if((i&(1<<j))==0)
{
dp[i|(1<<j)] += dp[i];
}
}
}
LL k = gcd(ans,f[tot]);
printf("%I64d/%I64d\n",ans/k,f[tot]/k);
}
return 0;
}