题目:F - Fireworks(三分+概率)
来自:2020-2021 ICPC区域赛南京站F题
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)
题意:
一个人造烟花,制作一个烟花需要n的时间,燃放烟花的时间是m(燃放一个烟花是m,燃放多个烟花也是m),但是由于此人手艺不精,烟花完美的概率是p。注意:烟花只有放了才知道是不是完美的。如果所有燃放的烟花中至少有一个是完美的,则结束。问实现该情况的最小期望
思路:
-
期望 = 一种情况的权值 * 其概率 + 另一种情况的权值 * 其概率 + …
-
假设一个Ex,表示多次造k个烟花后燃放,出现至少一个完美烟花的概率最大的最小期望,简称最优
-
形象的讲(不保证正确,但是好理解):每次都造k个后燃放出现完美烟花的概率是最大的,所以每次我们都只造k个烟花后燃放
-
所以:
E ( x ) = ( k × n + m ) × ( 1 − ( 1 − p ) k ) + ( k × n + m ) × ( 1 − p ) k + E ( x ) × ( 1 − p ) k E(x)=(k×n+m)×(1−(1-p)^k)+(k×n+m)×(1-p)^k+E(x)×(1-p)^k E(x)=(k×n+m)×(1−(1−p)k)+(k×n+m)×(1−p)k+E(x)×(1−p)k
分三块去理解:
1. ( k × n + m ) × ( 1 − ( 1 − p ) k ) 表示当前次制作 k 个烟花后燃放至少出现一次完美烟花的概率 ∗ 消耗的时间 2. ( k × n + m ) × ( 1 − p ) k 表示当前次制作 k 个烟花后燃放一次完美烟花都没有的概率 ∗ 单次消耗的时间 3. E ( x ) × ( 1 − p ) k 表示之前所有次都失败的概率 ∗ 之前用的所有时间 \begin{aligned} &1. (k×n+m)×(1−(1-p)^k) 表示当前次制作k个烟花后燃放至少出现一次完美烟花的概率*消耗的时间 \\ &2. (k×n+m)×(1-p)^k 表示当前次制作k个烟花后燃放一次完美烟花都没有的概率*单次消耗的时间 \\ &3. E(x)×(1-p)^k 表示之前所有次都失败的概率*之前用的所有时间 \end{aligned} 1.(k×n+m)×(1−(1−p)k)表示当前次制作k个烟花后燃放至少出现一次完美烟花的概率∗消耗的时间2.(k×n+m)×(1−p)k表示当前次制作k个烟花后燃放一次完美烟花都没有的概率∗单次消耗的时间3.E(x)×(1−p)k表示之前所有次都失败的概率∗之前用的所有时间
化简公式后发现:
E ( x ) = k × n + m 1 − ( 1 − p ) k E(x) = \frac{k×n+m} {1-(1-p)^k} E(x)=1−(1−p)kk×n+m
打表或求导可以看出该函数是个具有波谷的函数,用三分找到k次就可求出E(x)
代码:
#include<bits/stdc++.h>
#include<unordered_set>
#define HighPrecisionPrint(n) printf("%.11f\n", (n)) // 答案误差在10^-11次方内的打印
#define UNIQUE(arr) (arr).erase(unique((arr).begin(),(arr).end()),(arr).end()) //将重复的区域删除
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define all(arr) (arr).begin(), (arr).end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const double eps = 1e-7;
int t;
double m,n,p;/** 题意:一个人造烟花,制作一个烟花需要n的时间,* 燃放烟花的时间是m(燃放一个烟花是m,燃放多个烟花也是m),但是由于此人手艺不精,烟花完美的概率是p。* 注意:烟花只有放了才知道是不是完美的。* 如果所有燃放的烟花中至少有一个是完美的,则结束。* 问实现该情况的最小期望
*/
double Ex(int k)
{double ex = (n*k+m) / (1-pow(1-p,k));return ex;
}
void solve()
{cin>>n>>m>>p;p = p/1e4;double l = 1, r = 1000000000;while(r-l > eps){double mid1 = l + (r-l) / 3;double mid2 = r - (r-l) / 3;if(Ex(mid1) > Ex(mid2)) l = mid1;else r = mid2;}// 保障计算,如果最后l和r是左右两个点,并没有在波谷汇合,所以要手动枚举(while的情况不需要)// for(int i = l+1; i <= r; ++i)// ans = min(ans,Ex(i));HighPrecisionPrint(Ex(r));
}
int main()
{cin >> t;while (t--){solve();}return 0;
}