CF768D
题意:
\(K\) 种物品,求一次取一件把所有种类取遍的概率不小于 \(p/2000\) 的最小天数。
\(p\) 有 \(Q\) 次询问。
分析:
概率 \(dp\) ,而且选择的顺序没关系,因此二维就可以了
设 \(dp[i][j]\) 表示第 \(i\) 天,之前已经选择了 \(j\) 种物品的概率。
有两种情况:
- 第 \(i\) 天选择在第 \(i-1\) 天前已经选过了的物品的概率
此时,概率为 \(\frac{j}{n}\)
dp[i][j]+=dp[i-1][j]*j/n;
-
第 \(i\) 天选择了没有选过的物品
概率则是 \(\frac{n-j+1}{n}\) ,表示之前选过 \(j-1\) 个物品,就剩余 \(n-j+1\) 个物品没选择。
dp[i][j]+=dp[i-1][j-1]*(n-j+1)/n;
那么,怎么枚举天数呢?
最极限的情况是:\(1000\) 个物品,需要天数对应的概率不小于二分之一
我们设转移中最高天数为 \(m\)。
通过枚举 \(m\) (输出 \(dp[m][1000]\)),发现在这种条件下,需要的天数至少是 \(m=7500\) 天左右,而且转移的时间复杂度符合题意。
因此,我们设 \(m=10000\), 像上面一样进行转移。
最后,我们顺序查找:
当 \(dp[j][n] \geq p[i]/2000\) ,输出 \(j\) 即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=1e3+5;
double dp[N][M];
int K,Q;
int p[N],n,m=10000;
int main(){
cin>>n>>Q;
for(int i=1;i<=Q;i++){
scanf("%d",&p[i]);
}
dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
dp[i][j]=dp[i-1][j]*(double)j/n+dp[i-1][j-1]*(double)(n-j+1)/(n);
for(int i=1;i<=Q;i++){
int t=1;
for(int j=1;j<=m;j++) if(dp[j][n]<p[i]*0.0005) t++;
printf("%d\n",t);
}
// cout<<dp[m][n]<<endl;
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9