计蒜客信息学7月普及组模拟赛 - C 收获
题目
有 n 个地点,每个点有可能是果树或者亭子。初始有一个兴奋度 w,经过果树可以选择“收获” 一次,收获值为当前兴奋度 w ∗ a [ i ] w * a[i] w∗a[i],但是兴奋度会下降 k % k\% k%;经过亭子可以选择花费 w ∗ a [ i ] w*a[i] w∗a[i] 的收获值,但是兴奋度会上升 c % c\% c%。问从 1 走到 n,能获得最大收获值?
分析
好题
看起来跟背包问题很像,每次选择操作或者不操作,问最后最值。
首先经过一个地点选择收获或者不收获,是会改变当前兴奋值的,而兴奋值的改变还会影响后面的收获值,也就是当前操作会影响后面所有的操作,因为收获花费都跟 w 有关。也就是有后效性,正着DP肯定不行。
倒过来看,将 d p [ i ] dp[i] dp[i]表示为从第 i i i 个地点开始能获得最大收获值。
思维:将每次兴奋值的改变看作后面的所有收获的改变,也就是当前兴奋值改变后,可以看作不变,变的是后面的所有操作: d p [ i + 1 ] ∗ ( 1 + k % ) dp[i+1] *(1+k\%) dp[i+1]∗(1+k%)。两个等效的。
即:兴奋值一直保持在 w 的水平。
状态转移方程:
- 对于经过果树:
不收获的话,就是 d p [ i + 1 ] dp[i+1] dp[i+1],收获的话,就是看作后面所有操作都降低 k % k\% k%,而当前兴奋值还是不变,为 w ,最后加上当前收获的 w ∗ a [ i ] w*a[i] w∗a[i]。 d p [ i ] = m a x ( d p [ i + 1 ] , d p [ i + 1 ] ∗ ( 1 − k % ) + a [ i ] ∗ w ) dp[i] = max(dp[i+1], dp[i+1] * (1-k\%)+a[i]*w) dp[i]=max(dp[i+1],dp[i+1]∗(1−k%)+a[i]∗w) - 经过亭子:
道理是一样的:
d p [ i ] = m a x ( d p [ i + 1 ] , d p [ i + 1 ] ∗ ( 1 + c % ) − a [ i ] ∗ w ) dp[i] = max(dp[i+1], dp[i+1] * (1+c\%)-a[i]*w) dp[i]=max(dp[i+1],dp[i+1]∗(1+c%)−a[i]∗w)
前面初始化就是找到从后面数第一个果树,将他的值
w
∗
a
[
i
]
w*a[i]
w∗a[i]作为
d
p
dp
dp数组初始值。
因为亭子后面如果没有果树就没有意义,(反正后面也不会有收获)。
代码
#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;
}