计蒜客信息学7月普及组模拟赛 - C 收获
题目
有 n 个地点,每个点有可能是果树或者亭子。初始有一个兴奋度 w,经过果树可以选择“收获” 一次,收获值为当前兴奋度
,但是兴奋度会下降
;经过亭子可以选择花费
的收获值,但是兴奋度会上升
。问从 1 走到 n,能获得最大收获值?
分析
好题
看起来跟背包问题很像,每次选择操作或者不操作,问最后最值。
首先经过一个地点选择收获或者不收获,是会改变当前兴奋值的,而兴奋值的改变还会影响后面的收获值,也就是当前操作会影响后面所有的操作,因为收获花费都跟 w 有关。也就是有后效性,正着DP肯定不行。
倒过来看,将
表示为从第
思维:将每次兴奋值的改变看作后面的所有收获的改变,也就是当前兴奋值改变后,可以看作不变,变的是后面的所有操作:
。两个等效的。
即:兴奋值一直保持在 w 的水平。
状态转移方程:
-
对于经过果树:
不收获的话,就是
- ,收获的话,就是看作后面所有操作都降低
- ,而当前兴奋值还是不变,为 w ,最后加上当前收获的
- 。
-
经过亭子:
道理是一样的:
前面初始化就是找到从后面数第一个果树,将他的值
作为
数组初始值。
因为亭子后面如果没有果树就没有意义,(反正后面也不会有收获)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
#define max(a,b) a>b?a:b
typedef long long ll;
double n, k, c, w;
double a[N], id[N], dp[N];
int main()
{
scanf("%lf%lf%lf%lf", &n, &k, &c, &w);
for (int i = 1; i <= n; i++){
scanf("%lf%lf", &id[i], &a[i]);
}
int t = n;
while(id[t--] == 2) {}
dp[t + 1] = a[t + 1] * w;
for (int i = t; i >= 1; i--){
if(id[i] == 1){ // 果树
dp[i] = max(dp[i + 1], dp[i + 1] * (1.0 - k * 0.01) + a[i] * w);
}else{ // 亭子
dp[i] = max(dp[i + 1], dp[i + 1] * (1.0 + c * 0.01) - a[i] * w);
}
}
printf("%.2lf\n", dp[1]);
return 0;
}