计蒜客信息学7月普及组模拟赛 - C 收获(逆向DP)

计蒜客信息学7月普及组模拟赛 - C 收获

题目

有 n 个地点,每个点有可能是果树或者亭子。初始有一个兴奋度 w,经过果树可以选择“收获” 一次,收获值为当前兴奋度 w ∗ a [ i ] w * a[i] wa[i],但是兴奋度会下降 k % k\% k%;经过亭子可以选择花费 w ∗ a [ i ] w*a[i] wa[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 的水平。

状态转移方程:

  1. 对于经过果树:
    不收获的话,就是 d p [ i + 1 ] dp[i+1] dp[i+1],收获的话,就是看作后面所有操作都降低 k % k\% k%,而当前兴奋值还是不变,为 w ,最后加上当前收获的 w ∗ a [ i ] w*a[i] wa[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](1k%)+a[i]w)
  2. 经过亭子:
    道理是一样的:
    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] wa[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值