【CSP】202109-4 收集卡牌

题目大意:

小明抽卡,卡池共有n张卡,每张抽到的概率为pi,且每张重复的卡可以兑换成硬币,k个硬币可以兑换任意一张卡(硬币会攒起来在恰好可以兑换所有n张卡的时候一次性兑换)。问小明得到n张卡的期望抽卡次数是多少。(n<=16)

分析:

氪佬小明一看到n<=16就知道要状压dp,思路就是存储每一个状态的概率,答案就是对于所有满足条件的状态 求和{状态概率*状态抽牌数}。

故设dp[i][j]为:从初始状态到手牌为i,持有j个硬币的概率,状态转移方程可以表达为dp[i][j]=求和_u{ dp[i少一张牌u][j]*p[u] } + dp[i][j-1]*(i中所有手牌的概率总和)

但是需要注意的是,对于状态dp[i][j],如果此时硬币恰好能够兑换所有牌,那么对于i,硬币>j+k的概率均为0(因为此时小明已经兑换完所有的牌,给他的怨种朋友展示去了)。

至于为什么是j+k而不是j,是因为这<k个硬币是前面而非最后积攒到的,小明并不总是先不重复地抽完x张牌再抽取k*(n-x)个硬币来完成任务。

注意输出10位小数否则爆零,哈哈

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <string.h>

#define up(l,r,i) for(int i=l;i<=r;i++)
#define dn(l,r,i) for(int i=r;i>=l;i--)

typedef long long ll;

using namespace std;

inline int _max(const int& a,const int& b){return a>b?a:b;}
inline int _min(const int& a,const int& b){return a<b?a:b;}

int n,k;

double dp[1<<17][82];

double p[17],rp[17];

double remain(int x){
    double ret = 0;
    int cnt = 0;
    while(x){
        if(x%2 == 1)
            ret += p[cnt];
        cnt++;
        x>>=1;
    }
    return ret;
}

void p2(int x){
    int v[20],i=0;
    if(x == 0){
        cout<<0;
        return;
    }
    while(x){
        v[i++] = x%2;
        x >>=1;
    }
    dn(0,i-1,j){
        cout<<v[j];
    }
}

int g1n(int x){
    int ret = 0;
    while(x){
        ret += x%2;
        x>>=1;
    }
    return ret;
}

void solve(){
    dp[0][0] = 1;
    up(1,(1<<n)-1,i){
        //p2(i);
        up(0,n-1,j){
            if(1<<j > i) break;
            int v = i&(~(1<<j));
            if(v == i) continue;
            //p2(v);
            up(0,k*(n-g1n(i)+1)-1,l){
                dp[i][l] += dp[v][l]*p[j];
            }
        }
        up(1,k*(n-g1n(i)),l){
            dp[i][l] += dp[i][l-1]*remain(i);
        }
    }
}

int main()
{
    //freopen("y.in","r",stdin);

    //ios::sync_with_stdio(false);
    cin>>n>>k;

    up(0,n-1,i) {cin>>p[i];rp[i] = 1/p[i];}

    solve();

    double ans = 0;

    up(1,(1<<n)-1,i){
        int n1 = g1n(i);
        int coin = k*(n-n1);
        int card = coin + n1;
        ans += card * dp[i][coin];
        up(1,k-1,j){
            ans += (card+j)*dp[i][coin+j];
        }
    }

    printf("%.10f",ans);

    
    

    return 0;
}
AC代码

 

posted @ 2024-04-20 16:49  dudujerry  阅读(30)  评论(0编辑  收藏  举报