[xsy 3337][归并排序+概率dp]coin

题意
在这里插入图片描述

解法
这是一道神奇的题目。
显然,我们可以对每个假钞分别考虑。
f i , j f_{i,j} 表示第i种假钞最终持有者大于等于j个人的概率。
那么我们相当于求所有 f i , j f_{i,j} 前n大的和。
注意到,当i确定后,随着j的递增, f i , j f_{i,j} 递减。
于是考虑归并排序。
每次取出当前最大的种类,并在 O ( n ) O(n) 的时间内扩展到这个种类的下一个j。
时间复杂度: O ( n ( m + n ) ) O(n(m+n))

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 3005
double p[3005][305];
double f[305][Maxn];
inline void gao(int x){
	double pre=f[x][0];
	f[x][0]=0.0;
	for(register int i=1;i<=n;++i){
		double tmp=f[x][i];
		f[x][i]=f[x][i-1]*(1.0-p[i][x])+p[i][x]*pre;
		pre=tmp;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;++i)
		for(register int j=1;j<=m;++j){
			scanf("%lf",&p[i][j]);
			p[i][j]/=1000.00;
		}
	for(register int i=1;i<=m;++i){
		double tmp=1.0;
		f[i][0]=0.0;
		for(register int j=1;j<=n;++j)f[i][j]=p[j][i]+f[i][j-1]*(1.0-p[j][i]);
	}
	double Ans=0.0;
	for(register int i=1;i<=n;++i){
		double tmp=0.0;
		int id;
		for(register int j=1;j<=m;++j)
			if(f[j][n]>tmp){
				tmp=f[j][n];
				id=j;
			}
		Ans+=tmp;
		gao(id);
	}
    printf("%.10lf\n",Ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ezoilearner/article/details/85089537