标签:codeforces dp 组合数学
题面:
很久很久以前,一位老人和他的妻子住在蔚蓝的海边。有一天,这位老人前去捕鱼,他捉到了一条活着的金鱼。鱼说:“噢,老渔人!我祈求你放我回到海里,这样的话我保证给你n样礼物——任何你想要的礼物!”鱼给了老人一张礼物的清单并附上了礼物的价值。清单上的一些礼物可能会有相同的名称、不同的价值,也可能会有不同的名称、相同的价值。然而,清单上不会出现名称和价值都相同的礼物。
老人可以向鱼索要清单上的n个礼物。老人知道,如果他索要同一个名称s次,那么金鱼会等概率地随机选择该名称下的s样礼物。老人想要满足他贪心的妻子,所以他会选择价值最高的n样礼物。此外,如果有不同的方法选择最高价值的n样礼物,他会等概率地采用其中任意一个方法。
老人想知道,他能拿到n样价值最高的礼物的概率是多少。然而他不怎么擅长概率论,于是就来向你求助。
1 <= n,m <= 1000输入保证所有ki的总和不超过1000, 并且保证n不大于礼物总数。
题意:
给出m个物品组,现在可以要其中的n个物品;
你的策略是这样的:如果有一种方案,可能得到价值最高的n个物品,那么这种方案就是可取的,最终你会从所有可取的方案中随机取一种;
注意:每个物品只有一个,取这n个物品可以认为是同时的;
答案就是得到价值最高的物品的概率;
题解:
这题的题意有些绕。。。而且精度都吓得我上long double了;
实际上很容易想到的是,先按价值大小排序之后,前面有一些是必拿的;
而后面有一价值相同的东西才是有选择的;
那么对于一个物品组,我们需要关心的只有三种属性;
物品组中物品的总个数size,一定要选的物品数must,可能选的物品数can;
但是题中保证了同一个物品组只有一个相同物品,那么这里的can不是0就是1!
暂且不考虑可能选的物品的影响,答案就是1/ΠC[size i][must i];
组合数要提前处理出来,用个long double的1000*1000数组存;
这时我们其实可以暴搜所有方案,随机数据下速度也挺快,但很容易就被卡掉了;
设可以选的物品共有tot个,要从中选出cnt个;
暴搜的复杂度是C[tot][cnt]的,阶乘的增长速度。。。
所以想想不那么暴力的做法,如果物品组i的可选物品被选了,那么它对答案的贡献就由C[size i][must i]变成了C[size i][must i+1];
这之间差的系数我们很容易得到(可以展开拿出公式,也可以直接用处理好的C数组除);
答案求的是随机取方案的概率,也就是所有方案概率的平均数;
考虑一种方案,它的概率可以表示为1/ΠC[size i][must i]/cnt个不同的系数;
那设f[i][j]表示前i个系数,不同的j个乘在一起的和;
转移显然:f[i][j]=f[i-1][j]+系数*f[i-1][j-1];
然后得到的1/ΠC[size i][must i]/C[tot][cnt]/f[tot][cnt]就是答案了;
复杂度不太好算,但是反正不会超过1000*1000,大概也就是O(m^2)咯;
代码:
#include<iomanip> #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define N 1100 using namespace std; typedef long double ld; int val[N][N],size[N],a[N],tot; int n,m,tt,cnt,must[N],can[N]; ld C[N][N],fact[N],f[N][N],T[N],ans,mc; bool cmp(int a,int b) { return a>b; } void init() { C[0][0]=1; fact[0]=1; for(int i=1;i<N;i++) { fact[i]=fact[i-1]*i; C[i][0]=1; for(int j=1;j<=i;j++) { C[i][j]=C[i-1][j]+C[i-1][j-1]; } } } void DP() { f[0][0]=1; for(int i=1;i<=tt;i++) { f[i][0]=1; for(int j=1;j<=cnt;j++) { f[i][j]=f[i-1][j]+f[i-1][j-1]*T[i]; } } } int main() { int i,j,k; scanf("%d%d",&m,&n); init(); for(i=1;i<=n;i++) { scanf("%d",size+i); for(j=1;j<=size[i];j++) { scanf("%d",&val[i][j]); a[++tot]=val[i][j]; } sort(val[i]+1,val[i]+size[i]+1,cmp); } sort(a+1,a+tot+1,cmp); for(i=1,tt=0;i<=n;i++) { for(j=1;j<=size[i];j++) { if(val[i][j]>a[m]) must[i]++; else if(val[i][j]==a[m]) { T[++tt]=(ld)(must[i]+1)/(size[i]-must[i]); can[i]++; break; } else break; } } for(i=m,cnt=0;i>0;i--) { if(a[m]==a[i]) cnt++; else break; } for(i=1,mc=1;i<=n;i++) { mc*=C[size[i]][must[i]]; } DP(); ans=f[tt][cnt]/C[tt][cnt]/mc; cout<<fixed<<setprecision(10)<<ans<<endl; return 0; }
标签:codeforces dp 组合数学
原文地址:http://blog.csdn.net/ww140142/article/details/48109225