题目大意
给出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(
4n3∗22n3+m∗4n29∗2n3
)这个复杂度已经可以通过所有测试数据了。
代码
#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