Codeforces Round 1086 (Div. 2)——CF-2208-C. Stamina and Tasks(赛时 AC)(确实,乘法衰减,概率 dp,都是用这个反向居多) · znzryb
## 题目大意
**题目要求总结:**
给定多组测试。每组有 `n` 个任务,必须按编号从 `1` 到 `n` 依次处理。你初始体力为 `S=1`。
对每个任务 `i`(参数为 `c_i, p_i`)只有两种选择:
- 放弃:本任务得分不变,体力不变。
- 完成:本任务获得 `S * c_i` 分,然后体力变为`S = S * (1 - p_i / 100)`。
目标是在处理完所有任务后,使总得分最大。
输入规模:多组数据,`sum(n) <= 1e5`。
输出要求:每组输出一个实数最大值,误差满足 `1e-6` 即可。
**样例:**
```
Input
2
2
10 0
20 5
3
10 5
10 80
20 5
Output
30.0000000000
29.0000000000
```
**样例解释:**
- 第 1 组:做第 1、2 个任务最优。
第 1 个得 `1*10=10`,且 `p=0` 体力不变;第 2 个得 `1*20=20`;总分 `30`。
- 第 2 组:最优是做第 1 个,放弃第 2 个,做第 3 个。
做完第 1 个后体力 `S=1*(1-5/100)=0.95`;第 3 个得 `0.95*20=19`;总分 `10+19=29`。
## 思路讲解
这道题目,就是主要要注意到,就是我们不难发现,**最后一个数,我们肯定是要选的**,因为最后一个数对整体的贡献一定是有的,一定是正的。或者,你可以这样想,我们前面先选了一堆数,那么最后一个数一定是要选的,**因为选择他,对你的答案一定是增加的,你也不用考虑后续节点的情况,因为他后面也没有东西了**。
::: 赛时的时候是通过对拍注意到的这个事情

我们会发现,后面的这个数字被选的很多
```cpp
>>> bin(124)
'0b1111100'
>>>
```
:::
我们说白了,选不选一个数,我们就是怕这个后效性嘛,我们直接反过来,直接对所有选择的后缀进行这个乘法衰减。
但是,再前面的数字,那就不一定了,其对整体的贡献,就是新的答案 ans*(100-p)/100.0+A[i].c,如果说比原来的 ans 还小,那就开摆,否则就算了。
```cpp
for (ll i=N;i>=1;--i) {
ll c=A[i].c,p=A[i].p;
if (ans*(100-p)/100.0+A[i].c>ans) {
ans=ans*(100-p)/100.0+A[i].c;
}
}
```
## AC代码
AC
[https://codeforces.com/contest/2208/submission/366718643](https://codeforces.com/contest/2208/submission/366718643)
::: 源代码
```cpp
/**
* Problem: C. Stamina and Tasks
* Contest: Codeforces Round 1086 (Div. 2)
* Judge: Codeforces
* URL: https://codeforces.com/contest/2208/problem/C
* Created: 2026-03-14 23:58:07
* Author: Gospel_rock
* My blog: https://znzryb.com/
*
* Powered by AutoCp https://github.com/Pushpavel/AutoCp
*/
#include <bits/stdc++.h>
#define all(vec) vec.begin(),vec.end()
#define lson(o) (o<<1)
#define rson(o) (o<<1|1)
#define SZ(a) ((long long) a.size())
#define debug(var) cerr << #var <<" = ["<<var<<"]"<<"\n";
#define debug1d(a) \
cerr << #a << " = ["; \
for (int i = 0; i < (int)(a).size(); i++) \
cerr << (i ? ", " : "") << a[i]; \
cerr << "]\n";
#define debug2d(a) \
cerr << #a << " = [\n"; \
for (int i = 0; i < (int)(a).size(); i++) \
{ \
cerr << " ["; \
for (int j = 0; j < (int)(a[i]).size(); j++) \
cerr << (j ? ", " : "") << a[i][j]; \
cerr << "]\n"; \
} \
cerr << "]\n";
#define cend cerr<<"\n-----------\n"
#define fsp(x) fixed<<setprecision(x)
using namespace std;
using ll = long long;
using ull = unsigned long long;
using DB = double;
using i128 = __int128;
using CD = complex<double>;
static constexpr ll MAXN = (ll)1e6+10, INF = (1ll<<61)-1;
static constexpr ll mod = 998244353; // (ll)1e9+7;
static constexpr double eps = 1e-8;
const double pi = acos(-1.0);
ll lT,testcase;
/*
*
*/
struct C_p {
ll c,p;
};
void Solve() {
ll N;
cin >> N;
vector<C_p> A(N);
for (int i=0;i<N;++i) {
cin>>A[i].c>>A[i].p;
}
DB ans=0;
for (ll s=1;s<(1<<N);++s) {
DB lans=0;
DB S=1;
for (int i=0;i<N;++i) {
if (s>>i&1) {
lans+=S*A[i].c;
S*=(100.0-A[i].p)/100.0;
}
}
ans=max(lans,ans);
}
cout<<fsp(10)<<ans<<"\n";
#ifdef LOCAL
// for (int i=1;)
#endif
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
#ifdef LOCAL
cout.setf(ios::unitbuf); // 无缓冲流,方便我们调试
#endif
cin >> lT;
for (testcase=1;testcase<=lT;++testcase)
Solve();
return 0;
}
/*
1
7
75 74
76 50
66 13
53 30
74 46
75 67
53 91
*/
```
:::
## 心路历程(WA,TLE,MLE……)