CodeChef LEBOXES

5 篇文章 0 订阅
3 篇文章 0 订阅

有n个箱子,每个箱子打开有Pi的概率获得Vi的金币,1-Pi的概率获得1个钻石
这个人开完箱子后,会拿这些金币和钻石去买东西,m个东西,第i个东西需要Ci金币,Di钻石购买,问购买的东西数量的最大值的期望

Vi很大,但是注意到钻石数量很小,我们先做一个dp,f[i][j]表示我要买i个东西,用了不超过j个钻石,所需要的最少金币数

然后对n折半,搜出开出i个钻石的所有情况,然后枚举分别开了x,y个钻石,meet in middle计算期望

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define ld long double
using namespace std;

const int maxn = 35;

int n,m;
ld p[maxn];
int v[maxn],c[maxn],d[maxn];

void upd(int &x,const int &y){ if(x==-1||y<x) x=y; }
int now; int f[2][maxn][maxn];
void dp()
{
    memset(f,-1,sizeof f);
    now=0; f[now][0][0]=0;
    for(int i=1;i<=m;i++)
    {
        now=!now;
        for(int j=0;j<=i-1;j++) for(int k=0;k<=n;k++) if(f[!now][j][k]!=-1)
        {
            int &temp=f[!now][j][k];
            upd(f[now][j][k],temp);
            if(k+d[i]<=n) upd(f[now][j+1][k+d[i]],temp+c[i]);
            temp=-1;
        }
    }
    for(int i=0;i<=m;i++) for(int k=1;k<=n;k++) if(f[now][i][k-1]!=-1)
        upd(f[now][i][k],f[now][i][k-1]);
}
struct node
{
    ld p; int x;
    friend inline bool operator <(const node a,const node b){ return a.x<b.x; }
}zero;
vector<node>V1[maxn],V2[maxn];

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    zero.p=1.0,zero.x=0;

    int tcase; scanf("%d",&tcase);

    while(tcase--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) 
        {
            int x; scanf("%d%d",&v[i],&x);
            p[i]=(ld)x/100.0;
        }
        for(int i=1;i<=m;i++) scanf("%d%d",&c[i],&d[i]);

        dp();
        int hf=n>>1;

        int al=1<<hf;
        for(int i=0;i<=hf;i++) V1[i].clear();
        for(int i=0;i<al;i++)
        {
            node tmp=zero;
            int k=0;
            for(int j=1;j<=hf;j++)
            {
                if(i>>(j-1)&1) tmp.p*=p[j],tmp.x+=v[j];
                else tmp.p*=(1.0-p[j]),k++;
            }
            V1[k].push_back(tmp);
        }
        for(int i=0;i<=hf;i++) sort(V1[i].begin(),V1[i].end());

        al=1<<(n-hf);
        for(int i=0;i<=n-hf;i++) V2[i].clear();
        for(int i=0;i<al;i++)
        {
            node tmp=zero;
            int k=0;
            for(int j=hf+1;j<=n;j++)
            {
                if(i>>(j-hf-1)&1) tmp.p*=p[j],tmp.x+=v[j];
                else tmp.p*=(1.0-p[j]),k++;
            }
            V2[k].push_back(tmp);
        }
        for(int i=0;i<=n-hf;i++) sort(V2[i].begin(),V2[i].end());

        ld ans=0.0;
        for(int i=0;i<=hf;i++) if(V1[i].size())
            for(int j=0;j<=n-hf;j++) if(V2[j].size())
            {
                for(int k=1;k<=m&&f[now][k][i+j]!=-1;k++)
                {
                    int temp=f[now][k][i+j];
                    int y=(int)V2[j].size()-1; ld cc=0;
                    for(int x=0;x<(int)V1[i].size();x++)
                    {
                        while(y>=0&&V1[i][x].x+V2[j][y].x>=temp) cc+=V2[j][y].p,y--;
                        ans+=V1[i][x].p*cc;
                    }
                }
            }
        printf("%.4Lf\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值