HDU5816(2016多校第七场)——Hearthstone(暴力枚举,状态压缩)

来源:互联网 发布:中学生编程竞赛 编辑:程序博客网 时间:2024/04/24 08:54

Hearthstone

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 488    Accepted Submission(s): 206


Problem Description
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.

 

Input
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.
 

Output
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
23 1 21 23 5 101 1 1 1 1 1 1 1 1 1
 

Sample Output
1/346/273
 

Author
SYSU
 

Source
2016 Multi-University Training Contest 7
 

题意:你的牌堆里有两种卡,一种可以让你摸两张,一种可以造成一定伤害,一开始你可以抽一张卡,你造成的伤害能大于等于p的概率。

思路:比赛的时候队友想出用dp,然后过了。后来我想想,感觉这道题直接枚举抽卡牌的位置,算出可能的排列数除以总排列说不定也能过。

然后上午写了两个小时,各种手残终于写过了。

思路很简单,先预处理出,如果我摸到了k张伤害牌,有多少种伤害大于p的可能,直接暴力。

然后用状态压缩枚举n张抽卡牌在牌堆中的位置,模拟地求能拿到多少张伤害卡。根据预处理出的数组就能知道方法数。

再除以总排列数即可。

暴力大法好!

#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>using namespace std;#define MAXN 20long long dp[MAXN][1<<MAXN];long long L[MAXN];long long JC[MAXN];int p,n,m;int a[MAXN];void dfs(int cur,int B[]){    if(cur==m){        int ans=0;        int cnt=0;       // printf("<<%d %d>>",B[0],B[1]);        for(int i=0;i<m;i++){            if(B[i]) {                ans+=a[i];                cnt++;            }        }        if(ans>=p)            L[cnt]++;        return;    }    B[cur]=0;    dfs(cur+1,B);    B[cur]=1;    dfs(cur+1,B);}long long gcd(long long x,long long y){    if(y==0)        return x;    else        return gcd(y,x%y);}int main(){    int B[MAXN];    int t;    scanf("%d",&t);    JC[0]=1;    JC[1]=1;    for(int i=2;i<=MAXN;i++)        JC[i]=i*JC[i-1];    while(t--){        scanf("%d%d%d",&p,&n,&m);        long long res=0;        for(int i=0;i<m;i++)            scanf("%d",a+i);        memset(L,0,sizeof(L));        memset(B,0,sizeof(B));        dfs(0,B);        for(int i=1;i<=m;i++)            L[i]=L[i]*JC[i]*JC[m-i];        int comb=(1<<n)-1;        long long ans=0;        while(comb<1<<(n+m)){            vector <int> temp;            ans++;            for(int k=0;k<(n+m);k++)                if(comb&(1<<k))                    temp.push_back(k);            int x=comb&-comb,y=comb+x;            comb=((comb&~y)/x>>1)|y;            if(temp[0]!=0)            {                res+=L[1];                continue;            }            int cnt=1;            int s=1;//记录我还有的抽卡牌            int k=0;//现在已经抽完的最后一张卡的位置            for(int i=1;i<temp.size();i++){                if(temp[i]<=k){                    cnt++;                    s++;                    continue;                }                if(temp[i]-k<=s*2){                    s++;                    cnt++;                    int num=(temp[i]-k)/2;                    if((temp[i]-k)%2!=0){                        num++;                    }                    k+=num*2;                    s-=num;                }            }            cnt=min(cnt+1,m);            res+=L[cnt];        }        ans*=JC[m];        long long temp=gcd(ans,res);        ans/=temp,res/=temp;        printf("%I64d/%I64d\n",res,ans);    }}









0 0
原创粉丝点击