JZOJ4703 Buy

本文针对一个涉及概率选择和背包问题的组合优化问题进行了详细的解析。该问题包含两个主要部分:一是通过概率选择来获取资源(金钱和钻石),二是利用这些资源购买特定物品。文章首先描述了问题背景和目标,接着提出了一种指数级复杂度的解决方案,并通过具体的代码实现加以说明。
摘要由CSDN通过智能技术生成

题目大意

有n个袋子,每个袋子i有p[i]概率获得a[i]的钱,(1-p[i])获得1个钻石。
有m个物品,获取物品i需要c[i]的钱,和d[i]的钻石。
求获得物品的期望。
n,m≤30。 a[i],p[i],c[i]≤ 107

分析

考虑 O(2n) 暴力
很明显袋子只有两种取法。我们暴力出一种方案,设此时金钱数为V,钻石为T,概率为P,那么对答案的贡献就是P*可以获得的最多物品cnt。
现在暴力的主要问题是如何知道cnt。我们设f[i][j]表示有i个钻石,获得j个物品要花的最少钱数。很明显可以背包乱搞。到时候我们就知道cnt是多少了—— cnt=x|f[T][x]<=v

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int mx=1000000007;
struct rec
{
    int v;
    double p;
}b[31][1048576+1];//0也有用 
int a[40],c[40],d[40],f[35][35],i,T,lb,rb,l,r,lf,rf,stp,j,k,n,m,mid,T1;
double p[40],ans;
bool cmp(rec a,rec b)
{
    return a.v<b.v;
}
void dfs(int t,int v,double P,int x)
{
    if (x>stp)
    {
        b[t][0].v++;
        b[t][b[t][0].v].v=v;
        b[t][b[t][0].v].p=P;
        return;
    }
    dfs(t,v+a[x],P*p[x],x+1);
    dfs(t+1,v,P*(1-p[x]),x+1);
}
void df(int t,int v,double P,int x)
{
    if (x==stp)
    {
        fo(T,0,stp)
            fo(i,1,m)
            {
                lf=f[t+T][i]-v;
                rf=f[t+T][i+1]-v;
                l=1;
                r=b[T][0].v;
                while (l<r)
                {
                    mid=(l+r)/2;
                    if (b[T][mid].v>=lf) r=mid;
                    else l=mid+1;
                }
                lb=l;
                r=b[T][0].v;
                while (l<r)
                {
                    mid=(l+r+1)/2;
                    if (b[T][mid].v<rf) l=mid;
                    else r=mid-1;
                }
                rb=l;
                if (b[T][lb].v<lf) continue;
                if (b[T][rb].v>=rf) continue;
                ans=ans+double(i)*P*(b[T][rb].p-b[T][lb-1].p);
            }
        return;
    }
    df(t,v+a[x],P*p[x],x-1);
    df(t+1,v,P*(1-p[x]),x-1);
}
int main()
{
    scanf("%d",&T1);
    while (T1--)
    {
        scanf("%d %d",&n,&m);
        ans=0.0;
        fo(i,1,n) 
        {
            scanf("%d %lf",a+i,p+i);
            p[i]=p[i]/100.0;
        }
        fo(i,1,m) scanf("%d%d",c+i,d+i);
        fo(i,0,30) fo(j,0,30) f[i][j]=mx;
        f[0][0]=0;
        for (i=1;i<=m;i++)
            for (j=n;j>=d[i];j--)
                for (k=i;k;k--)
                    f[j][k]=min(f[j][k],f[j-d[i]][k-1]+c[i]);
        for (i=1;i<=n;i++)
            for (j=1;j<=m;j++) f[i][j]=min(f[i][j],f[i-1][j]);
        stp=n/3*2;
        dfs(0,0,1.0,1);
        fo(i,0,stp) 
        {
            sort(b[i]+1,b[i]+1+b[i][0].v,cmp);
            fo(j,2,b[i][0].v)
                b[i][j].p+=b[i][j-1].p;
        }
        df(0,0,1.0,n);
        printf("%.4lf\n",ans);
        fo(i,0,stp) b[i][0].v=0;
    }
}

不足

1,写个01背包都能错——一个物品多次重复计算;
2,二分时的continue没有涵盖所有条件,当lb>rb的时候没有考虑。
3,背包忘记f[i][j]可以等于f[i-1][j];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值