Description
有n个袋子和m个粮食(量词鬼畜,我是搬运工233)
第i个袋子里会有pi的概率装vi的金币,有1-pi的概率装一颗钻石。
每个粮食需要ci的金币和di的钻石才可以购买。
求购买粮食的期望个数。
n,m<=30,ci,vi<=100000000
Solution
首先,我们很显然可以预处理出Fi,j表示用i个钻石买j个粮食所需要的最小金币数。
然后,显然对于n<=30的情况,我们不能直接搜索出每个状态。
那我们可以考虑折半搜索。
把前a个袋子的所有状态用三元组S(p,s,v)表示有p的概率,s颗钻石和v个金币。(psv雾
因为每个袋子里只会有1颗钻石,所以我们按照钻石的数量吧所有三元组分组。
每组的s都相同,按v值排序,并且求p的前缀和。(后面会说用处)
然后,对于后面n-a个的状态S(p,s,v),我们枚举在前面用了t的钻石,总共买了i个粮食,那么我们会满足以下不等式:
F[s+t,i]<=v+v′<F[s+t,i+1]
其中v’表示在前面用的金币数。
显然我们可以得出v’的取值范围,然后二分出那些状态满足这个取值。
因为有前缀和,所以我们可以很轻易第求出p的和。
然后对答案贡献就可以了。
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 35
#define M 250005
using namespace std;
typedef double db;
typedef long long ll;
const int inf=2139062143;
struct note{ll v;db p;}g[N][M];
bool cmp(note x,note y) {return x.v<y.v;}
int ty,n,m,x,y,a,v[N],num[N];
ll f[N][M];
db sum[N][M],p[N],ans;
void dfs(int x,ll V,int d,db P) {
if (x>a) {g[d][++num[d]].v=V;g[d][num[d]].p=P;return;}
dfs(x+1,V+(ll)v[x],d,P*p[x]);dfs(x+1,V,d+1,P*(1-p[x]));
}
int search(int x,ll v) {
int l=1,r=num[x]+1;
while (l<r) {
int mid=(l+r)/2;
if (g[x][mid].v<v) l=mid+1;
else r=mid;
}
return l;
}
void find(int x,ll V,int d,db P) {
if (x>n) {
fo(t,0,a) fo(i,1,m) {
int l=f[d+t][i]-V,r=f[d+t][i+1]-V;
l=search(t,l);r=search(t,r)-1;
ans+=(sum[t][r]-sum[t][l-1])*P*i;
}
return;
}
find(x+1,V+(ll)v[x],d,P*p[x]);find(x+1,V,d+1,P*(1-p[x]));
}
int main() {
for(scanf("%d",&ty);ty;ty--) {
scanf("%d%d",&n,&m);memset(f,127,sizeof(f));
fo(i,0,n) f[i][0]=0;
memset(num,0,sizeof(num));ans=0;
fo(i,1,n) scanf("%d%lf",&v[i],&p[i]),p[i]/=100.0;
fo(i,1,m) {
scanf("%d%d",&x,&y);
fd(j,n,y) fd(k,m,1) f[j][k]=min(f[j][k],f[j-y][k-1]+x);
}
a=n*2/3.0;dfs(1,0,0,1);
fo(i,0,a) {
g[i][++num[i]].v=inf;
sort(g[i]+1,g[i]+num[i]+1,cmp);
fo(j,1,num[i]) sum[i][j]=sum[i][j-1]+g[i][j].p;
}
find(a+1,0,0,1);
printf("%.4lf\n",ans);
}
}