F - Fireworks(三分+概率)

题目:F - Fireworks(三分+概率)

来自:2020-2021 ICPC区域赛南京站F题

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

题意:

一个人造烟花,制作一个烟花需要n的时间,燃放烟花的时间是m(燃放一个烟花是m,燃放多个烟花也是m),但是由于此人手艺不精,烟花完美的概率是p。注意:烟花只有放了才知道是不是完美的。如果所有燃放的烟花中至少有一个是完美的,则结束。问实现该情况的最小期望

思路:

  1. 期望 = 一种情况的权值 * 其概率 + 另一种情况的权值 * 其概率 + …

  2. 假设一个Ex,表示多次造k个烟花后燃放,出现至少一个完美烟花的概率最大的最小期望,简称最优

  3. 形象的讲(不保证正确,但是好理解):每次都造k个后燃放出现完美烟花的概率是最大的,所以每次我们都只造k个烟花后燃放

  4. 所以:
    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(1p)k)+(k×n+m)×(1p)k+E(x)×(1p)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(1p)k)表示当前次制作k个烟花后燃放至少出现一次完美烟花的概率消耗的时间2.(k×n+m)×(1p)k表示当前次制作k个烟花后燃放一次完美烟花都没有的概率单次消耗的时间3.E(x)×(1p)k表示之前所有次都失败的概率之前用的所有时间
    化简公式后发现:

E ( x ) = k × n + m 1 − ( 1 − p ) k E(x) = \frac{k×n+m} {1-(1-p)^k} E(x)=1(1p)kk×n+m

打表或求导可以看出该函数是个具有波谷的函数,用三分找到k次就可求出E(x)

代码:

#include
#include
#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;
}

特别鸣谢:队友Gary解释思路,以及xjsc01提供的题解思路

你可能感兴趣的:(c++,icpc)