Buy(CC LEBOXES)

    xiaoxiao2026-06-13  9

    题目大意

    给出n个袋子和m个粮食,每个袋子有p[i]%的概率获得v[i]个金币,有1-p[i]%的概率获得1个钻石,而购买一个粮食要付出c[i]个金币和d[i]个钻石。求购买粮食的期望个数。 n,m<=30 v[i],c[i]<=10^7,p[i]<=100

    注意到n很小,也就是钻石数很小

    设f[i][j]表示拥有钻石数为j时,购买i个粮食所需的最小金币代价,这是个经典背包问题,可以O(n^3)解决。 如果直接暴力所有袋子的状态来计算期望,复杂度是O(n*2^n)。

    可以考虑折半搜索。

    先暴力处理前a个袋子的状态。 dfs(i,v,z,p)表示当前取到第i个(第i个袋子的状态还未知),当前有v个金币和z个钻石,此时的概率为p。 那么当i>a时,我们就退出dfs,并把dfs(a+1,v,z,p)的状态保存下来,t[z]表示拥有z个钻石的状态的个数,a[z][j].v表示拥有z个钻石的状态中第j个状态的金币数,a[z][j].p表示拥有z个钻石的状态中第j个状态的概率值,这样我们就把前a个袋子的状态都保存下来了。 然后对于所有的a[i]以a[i][j].v为关键字从小到大排序,排序后再处理a[i][j].s表示a[i][1~j].p的和。 dfs的复杂度为(2^a),排序的复杂度为O(2a*2^a) 然后再dfs一次处理剩余袋子的状态。 设结束状态为(v,z,p)(意义相同),计算贡献: 枚举购买的粮食数i,枚举前a个袋子获得的钻石数记作z1,前a个袋子获得的金币数记作v1,当然v1时不能枚举的,因为 f[i][z+z1]<=v+v1<f[i+1][z+z1] ,所以可以二分出v1在a[z1]的位置范围l~r,总概率就等于p*(a[z1][r].s-a[z1][l-1].s),期望贡献为总概率*i。 设b=n-a,后半部分的复杂度为O(m*a^2*2^b)(这里把二分的复杂度估计为a,即所有t[z1]=2^a,实际达不到) 当a=2n/3时复杂度为O( 4n322n3+m4n292n3 )这个复杂度已经可以通过所有测试数据了。

    代码

    #include<cstring> #include<algorithm> #include<cstdio> #include<cmath> #define fo(i,a,b) for(i=a;i<=b;i++) #define fod(i,a,b) for(i=a;i>=b;i--) using namespace std; const int maxn=30+5;const int ma=1048576; int i,j,n,t,m,l,n1,n2; int f[maxn][maxn],v[maxn],p[maxn],c[maxn],d[maxn]; int z[maxn]; struct ar{ int x;double p,sum; } a[21][ma]; double ans; void dfs(int x,int y,int y1,double s){ if (x>n1){ a[y1][++z[y1]].x=y,a[y1][z[y1]].p=s; return; }double s1=(double)p[x]/100; dfs(x+1,y+v[x],y1,s*s1),dfs(x+1,y,y1+1,s*(1-s1)); } int fen(int t,int x){ int l=1,r=z[t]; if (a[t][r].x<x) return r+1; while (l<r){ int m=(l+r)/2; if (a[t][m].x>=x) r=m;else l=m+1; }return l; } void df(int x,int y,int y1,double s){ if (x>n){ int t,i; fo(t,0,n1){ fo(i,1,m) if (f[i][t+y1]<f[m+1][0]){ int d=t+y1; int s1=f[i][d]-y,s2=f[i+1][d]-y;if (s2<=0) continue; int l=fen(t,s1),r=fen(t,s2);r--; ans+=(double)i*s*(a[t][r].sum-a[t][l-1].sum); }else break; } return; }double s1=(double)p[x]/100; df(x+1,y+v[x],y1,s*s1),df(x+1,y,y1+1,s*(1-s1)); } bool cmp(ar x,ar y){ return x.x<y.x; } int main(){ scanf("%d",&t); while(t){ t--; scanf("%d%d",&n,&m); fo(i,1,n) scanf("%d%d",&v[i],&p[i]); fo(i,1,m) scanf("%d%d",&c[i],&d[i]); memset(f,127/2,sizeof(f)); fo(i,0,n) f[0][i]=0; fo(i,1,m) fod(j,m,1) fo(l,d[i],n) f[j][l]=min(f[j][l],f[j-1][l-d[i]]+c[i]); ans=0; n1=n/3*2; memset(z,0,sizeof(z)); dfs(1,0,0,1); fo(i,0,n1) { sort(a[i]+1,a[i]+1+z[i],cmp); fo(j,1,z[i]) a[i][j].sum=a[i][j-1].sum+a[i][j].p; } df(n1+1,0,0,1); printf("%.4lf\n",ans); } }
    转载请注明原文地址: https://ju.6miu.com/read-1310495.html
    最新回复(0)