• 多校7 HDU5816 Hearthstone 状压DP+全排列


      1 多校7 HDU5816 Hearthstone 状压DP+全排列
      2 题意:boss的PH为p,n张A牌,m张B牌。抽取一张牌,能胜利的概率是多少?
      3 如果抽到的是A牌,当剩余牌的数目不少于2张,再从剩余牌里抽两张,否则全部拿完。
      4 每次拿到一张B牌,对boss伤害B[i]的值
      5 思路:dp[i]表示状态为i时的方案数
      6 先处理出所有状态下的方案,再枚举每种状态,如果符合ans+=dp[i]*剩余数的全排列
      7 当前集合里有a张A,b张B,那么还能取的牌数:a*2-a-b+1
      8 
      9 #include <bits/stdc++.h>
     10 using namespace std;
     11 #define LL long long
     12 const int inf = 0x3f3f3f3f;
     13 const int MOD =998244353;
     14 const int N =200010;
     15 #define clc(a,b) memset(a,b,sizeof(a))
     16 const double eps = 1e-7;
     17 void fre() {freopen("in.txt","r",stdin);}
     18 void freout() {freopen("out.txt","w",stdout);}
     19 inline int read() {int x=0,f=1;char ch=getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x*f;}
     20 int n,m,p;
     21 LL dp[1<<21];
     22 int B[21];
     23 LL fac[21]={1};
     24 int main(){
     25     fac[0]=1;
     26     for(int i=1;i<21;i++) fac[i]=fac[i-1]*i;
     27     int T,sum,a,b;
     28     scanf("%d",&T);
     29     while(T--){
     30         scanf("%d%d%d",&p,&n,&m);
     31         int N=n+m;
     32         for(int i=0;i<1<<N;i++) dp[i]=0;
     33         for(int i=n;i<N;i++) scanf("%d",&B[i]);
     34        
     35         dp[0]=1,sum=0,b=0,a=0;
     36         for(int i=0;i<1<<N;i++){
     37             if(dp[i]==0) continue;
     38             sum=0,a=0,b=0;
     39             for(int j=n;j<N;j++){
     40                 if(i&(1<<j)){
     41                    sum+=B[j];
     42                    b++;
     43                 }
     44             }
     45             if(sum>=p)continue;
     46             for(int j=0;j<n;j++){
     47                 if(i&(1<<j)){
     48                     a++;
     49                 }
     50             }
     51             if(a-b+1<=0) continue;
     52             for(int j=0;j<N;j++){
     53                 if((i&(1<<j))==0){
     54                     dp[i|(1<<j)]+=dp[i];
     55                 }
     56             }
     57         }
     58         LL Den=fac[N],Mol=0;
     59         for(int i=0;i<(1<<N);i++){
     60             a=0,b=0,sum=0;
     61             for(int j=n;j<N;j++){
     62                 if(i&(1<<j)){
     63                     b++;sum+=B[j];
     64                 }
     65             }
     66             if(sum<p) continue;
     67             for(int j=0;j<n;j++){
     68                 if(i&(1<<j)){
     69                     a++;
     70                 }
     71             }
     72             Mol+=(LL)dp[i]*fac[N-b-a];
     73         }
     74         LL g=__gcd(Mol,Den);
     75         printf("%I64d/%I64d
    ",Mol/g,Den/g);
     76     }
     77     return 0;
     78 }
     79 
     80 
     81 
     82 解法2标程:O(2^m)
     83 f[i][j]表示A拿i张,B拿j张的方案数
     84 方法用f[i][j]表示A类牌和B类牌分别抽到i张和j张,且抽牌结束前保证i>=j的方案数,这个数组可以用O(n^2)的dp预处理得到.
     85 接下来枚举B类牌的每个子集,如果这个子集之和不小于P,用k表示子集的1的个数,将方案总数加上取到这个集合刚好A类卡片比B类卡片少一(过程结束)的方案数:f[k-1][k] * C(n, k - 1) * (k - 1)! * k! * (n + m – 2*k + 1)! .
     86 如果子集包含了所有的B类卡片,则还需要再加上另一类取牌结束的情况,也就是取完所有牌,此时应加上的方案数为f[n][m] * n! * m! .
     87 最后的总方案数除以(n+m)!就是答案.
     88 
     89 
     90 #include <cstdio>
     91 typedef long long lli;
     92 
     93 int bc[1<<20], sum[1<<20], tmp[1<<20];
     94 int C[21][21];
     95 lli f[21][21], fact[21];
     96 
     97 lli gcd(lli a, lli b)
     98 {
     99     if (b == 0) return a;
    100     return gcd(b, a % b);
    101 }
    102 
    103 void init()
    104 {
    105     int i, j;
    106     bc[0] = 0;
    107     for (i=1; i<(1<<20); i++) bc[i] = bc[i^(i&-i)] + 1;
    108     fact[0] = 1;
    109     for (i=1; i<=20; i++) fact[i] = fact[i-1] * i;
    110     C[0][0] = 1;
    111     for (i=1; i<=20; i++)
    112     {
    113         C[i][0] = C[i][i] = 1;
    114         for (j=1; j<i; j++) C[i][j] = C[i-1][j] + C[i-1][j-1];
    115     }
    116     f[0][0] = f[0][1] = 1;
    117     for (i=1; i<=20; i++)
    118     {
    119         f[i][0] = 1;
    120         for (j=1; j<i; j++) f[i][j] = f[i-1][j] + f[i][j-1];
    121         f[i][i] = f[i][i+1] = f[i][i-1];
    122     }
    123 }
    124 
    125 int main()
    126 {
    127     lli a, b, d;
    128     int p, n, m, t, i;
    129     init();
    130     scanf("%d", &t);
    131     while (t --)
    132     {
    133         scanf("%d %d %d", &p, &n, &m);
    134         for (i=0; i<m; i++) scanf("%d", &tmp[1<<i]);
    135         sum[0] = 0;
    136         for (i=1; i<(1<<m); i++) sum[i] = sum[i^(i&-i)] + tmp[i&-i];
    137         a = 0;
    138         for (i=0; i<(1<<m); i++)
    139         {
    140             if (sum[i] >= p && bc[i] <= n + 1)
    141             {
    142                 a += C[n][bc[i]-1] * f[bc[i]-1][bc[i]] * fact[bc[i]-1] * fact[bc[i]] * fact[n+m-2*bc[i]+1];
    143                 if (bc[i] == m && bc[i] < n + 1) a += f[n][m] * fact[n] * fact[m]; 
    144             }
    145         }
    146         b = fact[n+m];
    147         d = gcd(a, b);
    148         printf("%I64d/%I64d
    ", a / d, b / d);
    149     }
    150     return 0;
    151 }
  • 相关阅读:
    《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)
    [xdoj]1303jlz的刷题黑科技
    [xdoj]1299&1300朱神的烦恼 朱神的序列
    [洛谷]p1996约瑟夫环 &xdoj1311
    [xdoj] 1310 DSKer的卡牌游戏
    [xdoj] 1301&1302 数字计数 数字计数的复仇
    随手快递app开发第十六天
    随手快递app开发第十五天
    随手快递app开发第十四天
    随手快递app开发第十三天
  • 原文地址:https://www.cnblogs.com/ITUPC/p/5764612.html
Copyright © 2020-2023  润新知