【CF183D】 T-shirt

洛谷链接

题目描述

你将在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[i1][j1][k]p[i][k]+f[i1][j][k](1p[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=0i1kf[n][k][i]+k=inif[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 ik之后只和 f [ i − 1 ] [ j ] [ k ] 和 f [ i − 1 ] [ j − 1 ] [ k ] f[i-1][j][k]和f[i-1][j-1][k] f[i1][j][k]f[i1][j1][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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值