[CODE FESTIVAL 2018]Sushi Restaurant
題意:有$n$個人,對每個人,他有$p_i$的概率飢餓值為$x_i$($1\leq i\leq m$),你現在要做$n$盤壽司,每盤壽司有一定的數量,當這$n$個人的飢餓值確定後他們會自己選擇最優的(人,壽司)配對方案使得$C=\sum\limits_{i=1}^n\left\lvert h_i-c_i\right\rvert$最小(設第$i$個人飢餓值為$h_i$,吃的壽司數量為$c_i$),現在要合適地選擇$c_{1\cdots n}$使得$C$的期望最小,輸出這個最小的期望
首先,如果確定了$h_{1\cdots n},c_{1\cdots n}$,那麼將它們都排序就可以得到最小的$C$,因為如果先把$h$從小到大排序,並且存在$c_i\gt c_j$,那麼交換$c_i,c_j$不會讓$C$變大
那麼我們要最小化$\sum\limits_{i=1}^n\sum\limits_{j=1}^mt_{i,j}\left\lvert c_i-x_j\right\rvert$,其中$t_{i,j}$為這些隨機變數中第$i$小的數是$x_j$的概率
設$u_{i,j}$為第$i$小的數$\leq x_j$的概率,那麼$t_{i,j}=u_{i,j}-u_{i,j-1}$,我們要算$u$
設$s$為$p$的字首和,因為恰有$k$個數$\leq x_j$的概率為$\binom nks_j^k(1-s_j)^{n-k}$,所以$u_{i,j}=\sum\limits_{k=i}^n\binom nks_j^k(1-s_j)^{n-k}$
最後的問題是要確定$c_i$使得$\sum\limits_{i=1}^n\sum\limits_{j=1}^mt_{i,j}\left\lvert c_i-x_j\right\rvert$最小,對每個$i$分開考慮,就是求一條折線上的最小值,而折線上的最小值只能在端點處取到,所以掃一遍即可
#include<stdio.h> typedef long double du; const du inf=9223372036854775807.; void fmin(du&a,du b){ if(b<a)a=b; } du pow(du a,int b){ du s=1; for(;b;b>>=1){ if(b&1)s*=a; a*=a; } return s; } int x[2010],m; du s[2010],c[2010][2010],t[2010][2010],st[2010]; du solve(du*t){ int i; du res; for(i=1;i<=m;i++){ st[i]=st[i-1]+t[i]; s[i]=s[i-1]+t[i]*x[i]; } res=inf; for(i=1;i<=m;i++)fmin(res,st[i]*x[i]-s[i]+s[m]-s[i]-(st[m]-st[i])*x[i]); return res; } int main(){ int n,Q,i,j; du res; scanf("%d%d%d",&n,&m,&Q); for(i=1;i<=m;i++){ scanf("%d%d",x+i,&j); s[i]=s[i-1]+j/(du)Q; } c[0][0]=1; for(i=1;i<=n;i++){ c[i][0]=1; for(j=1;j<=i;j++)c[i][j]=c[i-1][j-1]+c[i-1][j]; } for(i=n;i>0;i--){ for(j=1;j<=m;j++)t[i][j]=t[i+1][j]+c[n][i]*pow(s[j],i)*pow(1-s[j],n-i); } res=0; for(i=1;i<=n;i++){ for(j=m;j>0;j--)t[i][j]-=t[i][j-1]; res+=solve(t[i]); } printf("%.8lf",(double)res); }