概述
题目
The 2020 ICPC Asia Shenyang Regional Programming Contest
H. The Boomsday Project
大意:有一个人总是租单车。每次租车要 r 元。现在已知他将来m天的租车情况(每天要租p[i]辆车)。有 n 种优惠卡,第 i 种卡有个期限 d[i] 天(自买卡那天算起到第 d 天结束),可以免去这些天内的 k[i] 次租车钱,买卡要花 c[i] 元,一旦买了一张新卡,那之前的卡全部立即作废。问最少要花多少钱。
n<=500
sum ( p ) <=300000
分析
发现总租车数比较少,就把每次租车独立出来,按顺序进行dp。
先定义一下变量的意义:
cnt
代表着总的租车次数
a[i] 代表第 i 次租车所在的天。
f[i]
代表完成前 i 次租车所需的最少钱数。
ptr[i]
这个后面来解释
先是考虑最朴素的dp:
看伪代码以及里面的注释来理解:
先解释一下里面的 x 的意思:代表着在第 x 次租车结束之后办第 j 种卡,刚好可以免费到第 i 次租车。(若在第 (x-1) 次就不行了),具体的求法好说。
for i: [1 ~ cnt] { //顺序枚举第 i 次租车
f[i]= f[i-1]+r; //先是默认情况,啥优惠都不用
for j: [1 ~ n] //枚举使用第 j 种优惠
for t: [x ~ (i-1)] //枚举那些使用该种优惠可以影响到第 i 次的那些租车事件
f[i]= min(f[i], f[t]+c[j]) //看看在第 t 次租车结束后,买一张卡 j ,能否得到更优的效果。(即便有浪费免费次数也不管,反正更便宜了)
}
这样时间复杂度至少是 cnt * sigma(k,d) * (算 x 的时间),好像不太行
然后考虑优化:
注意到第三层循环,实际上是求 f[] 的区间最小值,又注意到 f[] 是递增的,所以那层循环可以直接去掉,最小值肯定是 f[x]。
for i: [1 ~ cnt] { //顺序枚举第 i 次租车
f[i]= f[i-1]+r;
for j: [1 ~ n] //枚举使用第 j 种优惠
f[i]=min(f[i], f[x])
}
这样循环就变成两层了,应该行了,接下来就是 x 的求法了。
会发现,随着 i 的增加,对于每个 j 来说, x 也是递增的。那么对每个 j 弄个指针标记记录,一直往右移就行了。这样时间复杂度就单独出来变成了 n * cnt。这也就是前面的 pre[] 数组了。
具体实现看下面的代码。
那这样总的时间复杂度就是 O(cnt * n)。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
long long r;
int d[505],k[505];
long long c[505];
int a[300005];
long long f[300005];
int cnt;
int ptr[505];
void solve()
{
scanf("%d%d%lld",&n,&m,&r);
for (int i=1; i<=n; i++) scanf("%d%d%lld",&d[i],&k[i],&c[i]);
for (int i=1,p,q; i<=m; i++) {
scanf("%d%d",&p,&q);
for (int i=1; i<=q; i++) a[++cnt]=p;
}
sort(a+1,a+cnt+1); //输入不是按天递增的,所以要排序
for (int i=1; i<=cnt; i++) {
f[i]=f[i-1]+r;
for (int j=1,x; j<=n; j++) {
while (a[i]-a[ptr[j]+1]>=d[j] || i-ptr[j]>k[j]) ptr[j]++;
f[i]=min(f[i], f[ptr[j]]+c[j]);
}
}
printf("%lldn",f[cnt]);
}
int main()
{
solve();
return 0;
}
最后
以上就是温柔小霸王为你收集整理的H. The Boomsday Project (dp)题目分析代码的全部内容,希望文章能够帮你解决H. The Boomsday Project (dp)题目分析代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复