思路
能够赢的状态有:
1. 抽一张B就赢
2. 抽一定数量的A、配合B
Tip:
1. 如果造成的伤害已经
≥p
,那么就直接乘上剩下的全排列即可
2. 从已经抽了的牌数可以推出能够再抽的牌数:
leftCard=usedA−usedB+1
从小到大枚举状态S,判断S是否合法,即S的方案数不为0且还能摸牌,然后通过S去更新S+i(i∉S,即后续摸牌状态)状态的值
代码
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=1e3+50;
const int Mod=1000000007;
const double PI=acos(-1);
typedef long long ll;
using namespace std;
int n,p,a,b,k;
ll dp[1<<22];
int sb[100];
ll jc[100];
ll fz;
void init(){
jc[0]=jc[1]=1;
for(int i=2;i<=20;i++){
jc[i]=jc[i-1]*i;
}
}
inline bool getBit(int x,int i){
return (x>>i)&1;
}
int countBit(int x,int n){
int ret=0;
for(int i=0;i<n;i++){
if (x&(1<<i)) ret++;
}
return ret;
}
void solve(){
mem(dp,0);
dp[0]=1;
for(int i=0;i<(1<<k);i++){
if (dp[i]==0) continue;
int cb=0,ca=0,sum=0;
for(int j=a;j<k;j++){
if (getBit(i,j)){
cb++; sum+=sb[j];
}
}
//不用再抽牌
if (sum>=p) continue;
ca=countBit(i,a);
//没有后续状态 : AABBB ABB B
if (ca-cb+1<=0) continue;
for(int j=0;j<k;j++){
if (!getBit(i,j)){
dp[i^(1<<j)]+=dp[i];
}
}
}
for(int i=0;i<(1<<k);i++){
if (dp[i]==0) continue;
int cb=0,ca=0,sum=0;
for(int j=a;j<k;j++){
if (getBit(i,j)){
cb++; sum+=sb[j];
}
}
ca=countBit(i,a);
if (sum>=p) fz+=jc[k-ca-cb]*dp[i];
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
init();
int T; scanf("%d",&T);
for(int cs=1;cs<=T;cs++){
scanf("%d %d %d",&p,&a,&b);
k=a+b;
for(int i=a;i<k;i++){
scanf("%d",sb+i);
}
fz=0;
solve();
ll fm=jc[k];
if (fz==0) printf("0/1\n");
else {
ll gc=__gcd(fz,fm);
printf("%lld/%lld\n",fz/gc,fm/gc);
}
}
return 0;
}