[CF183D]T-shirt

题面在这里

description

\(n\)个人,\(m\)种大小的衣服,第\(i\)个人是第\(j\)种大小的概率是\(p_{i,j}\)
选择\(n\)件衣服,最大化合适衣服的期望。

data range

\[n≤3000,m≤300 \]

solution

官方题解入口

首先仍然把每种T-shirt分开考虑,即如果我们对于某种大小的衣服选的数量一定,
那么最后这种大小的衣服合适的数量期望是一定的;

考虑\(DP\),对于第\(k\)种大小的衣服,
\(f[i][j]\)表示前\(i\)个人中适合人数为\(j\)的期望,那么有

\[f[i][j]=f[i-1][j-1]\times p[i][j]+f[i-1][j]\times (1-p[i][k]) \]

\(g[i]\)表示这种大小的衣服固定选\(i\)件的期望,那么有

\[g[i]=\sum_{j=0}^{i}j\times f[n][j]+\sum_{j=i+1}^{n}i\times f[n][j] \]

于是我们可以把每一种大小看成一组物品,用分组背包来做这道题,
复杂度为\(O(n^2m)\)

如果我们能够看到

\[g[i+1]-g[i]=\sum_{j=i+1}^{n}f[n][j]=1-\sum_{j=0}^{i}f[n][j] \]

差值递减,那么我们可以知道\(g[i]\)函数单峰

正因如此,我们动态地看这个挑选衣服的问题,
如果我们每次从\(m\)中衣服里挑选\(\Delta=g[i+1]-g[i]\)(\(i\)表示当前选择的衣服件数)
最大的一个,那么可以保证**之后所有选择的$\Delta'\le\Delta \(** 因此这样贪心选出的\)Ans=\sum\Delta$一定不会比最优方案更

实现:
首先\(O(nm)\)地预处理和算出每个尺寸的\(\Delta=g[1]-g[0]=1-f[n][0]\)
之后每次选出一个最大的\(\Delta\),将该种颜色衣服的\(\Delta\)重新计算,
根据预处理\(\Delta\)的复杂度可以分为\(O(n^2+nm)\)\(O(n^2+nlogm)?\)
这里只需要\(O(n^2+nm)\)即可

code

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e8;
const int N=3010;
const int M=305;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

il void file(){
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
}

int n,m;dd p[N][M],d[M],f[M][N],c[M],l[N],ans;
il void init(){
	for(RG int i=1;i<=m;i++){
		f[i][0]=1;
		for(RG int j=1;j<=n;j++)
			f[i][j]=f[i][j-1]*(1-p[j][i]);
		d[i]=1-f[i][n];
	}
}

il void update(int k){
	swap(f[k],l);f[k][0]=0;//注意此处
	for(RG int i=1;i<=n;i++)
		f[k][i]=l[i-1]*p[i][k]+f[k][i-1]*(1-p[i][k]);
	c[k]++;d[k]-=f[k][n];
}

int main()
{
	n=read();m=read();
	for(RG int i=1;i<=n;i++)
		for(RG int j=1;j<=m;j++)
			p[i][j]=read()*1.0/1000;
	init();
	for(RG int i=1,k;i<=n;i++){
		k=0;
		for(RG int j=1;j<=m;j++)
			if(d[k]<d[j])k=j;
		if(!k)break;
		ans+=d[k];update(k);
	}
	printf("%.12lf\n",ans);
	return 0;
}
posted @ 2018-04-08 22:35  cjfdf  阅读(202)  评论(0)    收藏  举报