【线性DP】luogu_P1412 经营与开发

题意

依次经过\(n\)个地点。有个钻头,初始能力值为\(w\)。地点分为\(2\)类:资源型和维修型。

1.资源型:若选择开采,则得到\(a_i*p\)的金钱,之后钻头损耗\(k\%\)
2.维修型:若选择维修,则支付\(b_i*p\)的金钱,之后钻头修复\(c\%\)

求最大化的收入(收入\(-\)支出)

注:维修后钻头的能力值可以超过初始值、金钱可以透支。

数据范围:\(1\leq n\leq 10^5,0\leq k,c,w,a_i,b_i\leq 100,\)保证答案不超过\(10^9\)

思路

考虑\(dp\)
发现地点肯定是要枚举的,如果再枚举一个钻头的能力值,复杂度会很高。
考虑不枚举能力值怎样做。发现当前能力值都是和之前的能力值有关,可以看成若干个\((1+k\%)\)和若干个\((1+c\%)\)乘起来,它们对于现在的答案影响也只在于这若干个式子,所以当前的能力值没有必要枚举。

正难则反。考虑当前的地点\(i\),对于之后的影响。当前如果开采,能力值会乘上\((1+k\%)\),所以之后的每一处开采和修复时的能力值也都会乘上\((1+k\%)\),即\(f_{i+1}\)乘上\((1+k\%)\),维修同理。
\(n\)做到\(1\),发现还没有此时默认的初始能力值是\(1\),所以最后答案要乘上\(w\),代表初始能力值对之后答案的影响。
那么设\(f_i\)为后\(i\)个地点的最大价值,可得:
\(f_i=max(f_{i+1},x_i+f_{i+1}*(1-0.01*k))\)
\(f_i=max(f_{i+1},f_{i+1}*(1+0.01*c)-x_i)\)

代码

#include<cstdio>
#include<algorithm>
using namespace std;

int type[100001], x[100001];
int n, k, c, w;
double f[100001];
 
int main() {
    scanf("%d %d %d %d", &n, &k, &c, &w);
    for (int i = 1; i <= n; i++)
        scanf("%d %d", &type[i], &x[i]);
    for (int i = n; i >= 1; i--) {
        if (type[i] == 1) f[i] = std::max(f[i + 1], x[i] + f[i + 1] * (1 - 0.01 * k));
        else f[i] = std::max(f[i + 1], f[i + 1] * (1 + 0.01 * c) - x[i]);
    }
    printf("%.2lf", f[1] * w);
}
posted @ 2020-11-30 15:54  nymph181  阅读(89)  评论(0)    收藏  举报