优化转移DP

Problem - H - Codeforces

题意

Aloha 要骑单车,可以单独花费 \(r\) 元骑 1 次,也可以购买某一种单车卡,第 \(i\) 种单车卡 \(c_i\) 元,若在第 \(t\) 天购买,可以在 \([t,t+d_i-1]\) 天使用,并且最多使用 \(k_i\) 次

给出 Aloha 一段时间内的单车使用情况,求出他需要的最小花费

给出

  1. \(n\;(1<=n<=500)\), 单车卡的种类

  2. \(m\;(1<=m<=10^5)\), \(m\) 条使用记录

  3. \(r\;(1<=r<=10^9)\), 单独骑 1 次的花费

  4. \(n\) 行,每行有 \(1<=d_i,k_i,c_i<=10^9\), 第 \(i\) 种单车卡的有效期、使用次数、价格

  5. \(m\) 行,每行有 \(0<=p_i<=10^9,\;0<=q_i<=3e5,\;\sum\limits_{i=1}^mq_i<=3e5\)

    表示在第 \(p_i\) 天骑了 \(q_i\) 次

思路

  1. 看上去就是 DP,观察数据范围,很多数据的值域过大,只有 \(n\) 和 \(\sum q_i\) 可能与 DP 复杂度有关

  2. 求出每次骑的时间 \(a[i]\), 一共骑了 cnt 次,并按时间排序;

    设 \(f[i]\) 为骑完前 \(i\) 次的最小花费,\(ptr[i][j]\) 为如果第 \(i\) 次可以用到第 \(j\) 个卡,则最早在第 \(ptr[i][j]\) 次骑完后买卡

    可列出朴素DP转移

    for (int i = 1; i <= cnt; i++)
    {
    f[i] = f[i - 1] + r;//初始化为不买卡直接骑
    for (int j = 1; j <= n; j++)
    {
    auto [d, k, c] = b[j];
    for (int w = ptr[i][j]; w < i; w++)
    f[i] = min(f[i], f[w] + c);
    }
    }
  3. 由于 \(f[i]\) 是单调不减的,因此 \(f[i]=min(f[i],f[w]+c)\) 中 \(w=ptr[i][j]\) 时是最优的(也可以感性地感受一下,这样卡的利用率更高,这样好像倒推更好理解一点,有时间试试)

  4. 接下来就是需要求出 \(ptr[i][j]\),第一维可以优化掉,\(ptr[j]\) 表示第 i 次骑车时,最早需要 \(ptr[j]\)次买第 j 种卡才能覆盖第 i 次

    随着 \(i\) 增大,\(ptr[j]\) 不会回退,因此可以暴力 check,双指针维护

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n" typedef long long ll;
typedef pair<int, int> PII; const int N = 3e5 + 10;
int n, m;
ll r;
struct Cards
{
ll d, k, c;
}b[510];
int a[N];//第i次的时间
int cnt;
ll f[N];//前i次的最小代价
int ptr[510]; bool check(int now, int card)
{
int last = ptr[card];
auto [d, k, c] = b[card];
if (last == now)
return true;
if (a[last] + d - 1 < a[now])
return false;
if (now - last + 1 > k)
return false;
return true;
} int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m >> r;
for (int i = 1; i <= n; i++)
cin >> b[i].d >> b[i].k >> b[i].c;
for (int i = 1; i <= m; i++)
{
int p, q;
cin >> p >> q;
while(q--)
a[++cnt] = p;
}
sort(a + 1, a + cnt + 1);
for (int i = 1; i <= n; i++)
ptr[i] = 1;
for (int i = 1; i <= cnt; i++)
{
f[i] = f[i-1] + r;
for (int j = 1; j <= n; j++)
{
auto [d, k, c] = b[j];
// for (int w = ptr; w < i; w++)
// f[i] = min(f[i], f[w] + c);
// 因为f[i]单调递增,因此取w=x最小,ptr为在第ptr次后买优惠券,正好可以覆盖到第i次,双指针更新ptr
while(!check(i, j))
ptr[j]++;
f[i] = min(f[i], f[ptr[j] - 1] + c);
// cout << i << ": " << j << " " << ptr[j] << endl;
}
}
cout << f[cnt] << endl;
return 0;
}

最新文章

  1. SVN版本号打包脚本工具
  2. 给div设置background-color: rgba(0, 0, 0, 0.2)属性,并加了css3动画--opacity动画淡出动画,之后div子元素的字体会抖一下
  3. MMS彩信字符集(字符编码)
  4. python 实现web框架simfish
  5. maven pom.xml报错
  6. 转 SQL Server中关于的checkpoint使用说明
  7. (转载)MySQL关键字GROUP BY的使用
  8. Android-现场保护
  9. Static关键字的作用及使用
  10. C# 关于委托的小例子
  11. APUE 3 -- 信号 (signal)&lt;II&gt;: 可靠信号
  12. python 3.6 MJ小工具
  13. 【BZOJ2301】【HAOI2011】Problem B(莫比乌斯反演)
  14. 神经网络_线性神经网络 1 (Nerual Network_Linear Nerual Network 1)
  15. 让 ComboBox 的每个栏目显示不同颜色
  16. Nginx 如何通过连接池处理网络请求
  17. CURL错误代码及含义
  18. 加强对HEAD 请求的处理(转贴)
  19. crosss compile VLC with OpenMAX on ARM board(RockChip RK3399),in order to use Hard Acceleration when decode video
  20. python之模块csv之CSV文件一次写入多行

热门文章

  1. python 中变量的命名规则与注释
  2. js将时间戳转成时间格式
  3. Vue element 自定义表单验证(验证手机号)
  4. 用友开发者中心全新升级,YonBuilder移动开发入门指南
  5. 如何指定多个项目的 InternalsVisibleTo
  6. Kafka相关面试题及答案
  7. linux基础:1、linux简介、虚拟化软件的安装与配置、Xshell的安装与配置
  8. VS2019注册码
  9. vue-3 this概念
  10. Java实现BP神经网络MNIST手写数字识别