NOIP模拟赛-小奇挖矿
NOIP模拟赛:小奇挖矿
hzwer大佬果然是甜甜私房猫爱好者...
题目背景
小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值 w)的飞船,按既定
路线依次飞过喵星系的 n 个星球。
问题描述
星球分为 2 类:资源型和维修型。
资源型:含矿物质量 a[i],若选择开采,则得到 a[i]p 的金钱,之后钻头
损耗 k%,即 p=p(1-0.01k)
维修型:维护费用 b[i],若选择维修,则支付 b[i]p 的金钱,之后钻头修
复 c%,即 p=p(1+0.01c)(p 为钻头当前能力值)
注:维修后钻头的能力值可以超过初始值
请你帮它决策最大化这个收入。
数据范围
对于 30%的数据 n<=100
对于 50%的数据 n<=1000,k=100
对于 100%的数据 n<=100000,0<=k,c,w,a[i],b[i]<=100
保证答案不超过 \(10^9\)
题解
很容易就能想到一个三维DP:设\(f_{i,j,k}\)表示考虑前i个元素,选取j个资源型,k个维修型的最大价值。
由于每个维修点和资源点对钻头耐久的影响是相同的,所以只需要统计个数就能很方便的计算下一个点的贡献。
但是这样做的时间复杂度是\(n^3\)显然会超时,考虑优化。
为什么我们必须要枚举三维dp呢?是因为前面的决策会对后续点的贡献造成影响。
这个影响的大小可以很方便地利用乘法分配律进行维护,但很可惜我们并不知道后面的最大价值...
[异议?!]从后往前dp不就可以知道后面的价值了吗?
由于每一次转移都不需要关心之前的决策,只需要维护价值;所以每一个决策都是相互独立的;
这样我们就可以回归纯粹的一维线性状态dp,用一个近乎贪心的做法完美地解决本题。
设f[i]为后i个点的最大价值,得到转移方程:
\(f_i=\max (f_{i-1}\times(1-c\%)+f_i,f_{i-1})\)
代码时间
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n,k,c,w;
struct node{
double a,b;
}d[100005];
double f[100005];
int main(){
freopen("explo.in","r",stdin);
freopen("explo.out","w",stdout);
n=read(),k=read(),c=read(),w=read();
for(int i=1;i<=n;i++){
d[i].b=read();
d[i].a=read();
}
for(int i=n;i>=1;i--){
if(d[i].b==1)f[i]=max(f[i+1],f[i+1]*(1-0.01*k)+d[i].a);
else f[i]=max(f[i+1],f[i+1]*(1+0.01*c)-d[i].a);
}
f[1]=f[1]*w;
printf("%.2lf",f[1]);
}

浙公网安备 33010602011771号