题目描述
你将在CodeForces的一个nnn人团队实习,n个工程师由1到nnn编号。你决定给每个工程师一个纪念品:一件来自你的国家的T恤(T恤在CodeForces很受欢迎)。不幸的是,你不知道n个工程师各自衣服的尺寸。一共有1到m共m种不同的尺寸,并且每个工程师只适合一个尺寸。
你不知道每个工程师的尺寸,所以你询问你的朋友Gerald。很遗憾,他也不知道每个工程师的尺寸,但他知道对于第iii个工程师,适合第j种T恤的概率。
最后你带来了n件T恤(这n件T恤可以是任意组合,你也可以带多件同样尺寸的衣服),在你准备T恤的时候并不知道每个工程师的尺寸,所以你只能根据Gerald提供的概率决定你所带的T恤。
你的任务是最大化收到适合自己的衣服的工程师数量的期望值。
当你到达办公室后,你会询问每个工程师他适合的T恤的尺寸,如果你有那个尺寸的衣服,你就会给他一件,否则就不给他T恤。你会从1号问起,一直问到n号
Sol
这是一道 期望dp 好题
考虑暴力的做法:
裸暴力是暴力枚举T恤的情况,然后暴搜求出这时的期望
考虑DP , 可以列出每一种派出方案的答案的贡献来源,根据期望的 线性 性 , 如果一种大小的T恤做的个数被确定下来 , 那么任意情况下对答案的贡献都是一定的,可以把这个看成是一个物品,用分组背包来最优化答案
具体来说:
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示前 i i i个人中有 j j j个需要T恤大小为 k k k的概率
设 g [ i ] [ j ] g[i][j] g[i][j] 表示做了 i i i件大小为 j j j 的T恤对答案产生的贡献
那么:
f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j − 1 ] [ k ] ∗ p [ i ] [ k ] + f [ i − 1 ] [ j ] [ k ] ∗ ( 1 − p [ i ] [ k ] ] ) f[i][j][k]= f[i-1][j-1][k]*p[i][k]+f[i-1][j][k]*(1-p[i][k]]) f[i][j][k]=f[i−1][j−1][k]∗p[i][k]+f[i−1][j][k]∗(1−p[i][k]])
g [ i ] [ j ] = ∑ k = 0 i − 1 k ∗ f [ n ] [ k ] [ i ] + ∑ k = i n i ∗ f [ n ] [ k ] [ i ] g[i][j]=\sum_{k=0}^{i-1} k*f[n][k][i]+\sum_{k=i}^n i*f[n][k][i] g[i][j]=∑k=0i−1k∗f[n][k][i]+∑k=ini∗f[n][k][i]
最后用一个分组背包求出答案即可
考虑优化: 感性理解一下可以发现 g g g 的增长速度是越来越慢的 , 因为一个大小的T恤做多了就需要很多人都要求是该大小才会有贡献,那么就更加容易出现没有几个人要这种大小的T恤的尴尬场景,所以同一种大小的做多了可能会不优
但是 g g g依然是递增的,因为一个 g g g只考虑了一种T恤 , 显然T恤总数越多期望越高
那么就可以贪心了 , 考虑每一次都多做一件T恤,然后把大小选择为当前增加值能够最大的大小
具体来说就是先算出每一种大小的T恤只做一件时的期望 , 然后选择一种大小后把 g g g 给更新一层
怎么更新呢? 考虑到 g g g 的更新和 f f f 有关,我们必须先更新 f f f
三维数组开不下,不能直接把所有的
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]都存下来,但是我们不能省掉
i
i
i 这一维,因为省掉了就会算重
可以发现
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]的转移在确定
i
和
k
i和k
i和k之后只和
f
[
i
−
1
]
[
j
]
[
k
]
和
f
[
i
−
1
]
[
j
−
1
]
[
k
]
f[i-1][j][k]和f[i-1][j-1][k]
f[i−1][j][k]和f[i−1][j−1][k]有关,也就是
j
j
j只会向前考虑一层,于是我们可以把
j
j
j这一维省掉
至于 g g g的更新,小于 i i i的我们用算出来的 f f f进行计算,大于的由于每次乘的东西是 i i i,又显然最后这一系列的 f f f的和为 1 ,那么容斥算一下就得到了后一段的和,于是就愉快地做完了
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
const int N=3001;
const int M=301;
typedef long double ldb;
typedef double db;
typedef long long ll;
db p[N][M];
int n,m;
db ans;
db f[N][M];
#define Max(a,b) ((a)>(b)? (a):(b))
db g[N][M];
int h[M];
db sum[N];
db S[N][M];
db dp[M][N];
int main()
{
n=read();m=read();
for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) p[i][j]=1.000*read()/1000.000;
/**************************************************************************************************/
for(register int k=1;k<=m;++k) {f[0][k]=1.00;for(register int i=1;i<=n;++i) f[i][k]=f[i-1][k]*(1-p[i][k]);}
for(register int j=1;j<=m;++j) {
g[1][j]=1.000-f[n][j],h[j]=1,S[0][j]=f[n][j];
}
ans=0;
for(register int i=1;i<=n;++i){
register int max_pos=0;register db maxn=0;
for(register int j=1;j<=m;++j){
register db delta=g[h[j]][j]-g[h[j]-1][j];
if(!max_pos||delta>maxn) max_pos=j,maxn=delta;
}
ans+=maxn;register int k=++h[max_pos];
for(register int j=0;j<=n;++j) sum[j]=f[j][max_pos],f[j][max_pos]=0.0;
for(register int j=1;j<=n;++j) f[j][max_pos]=sum[j-1]*p[j][max_pos]+f[j-1][max_pos]*(1.00-p[j][max_pos]);
S[k-1][max_pos]=f[n][max_pos];db SUM=0.0;
for(register int j=0;j<k;++j) g[k][max_pos]+=1.00*j*S[j][max_pos],SUM+=S[j][max_pos];
g[k][max_pos]+=1.00*k*(1.00-SUM);
}
printf("%.9lf\n",ans);
}