优化转移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;
}

2020icpc沈阳H的更多相关文章

  1. 2020ICPC沈阳站C题 Mean Streets of Gadgetzan

    大致题意 原题链接 翻译 \(有n个逻辑变量 请你分别对它们赋值 使其满足m个命题\) \(命题有四种格式:\) 单独数字x 表示第x个逻辑变量为真 ! + 数字x 表示第x个逻辑变量为假 若干个数字 ...

  2. 每日一刷(2018多校水题+2016icpc水题)

    11.9 线段树 http://acm.hdu.edu.cn/showproblem.php?pid=6315 求逆序对个数 http://acm.hdu.edu.cn/showproblem.php ...

  3. 2019~2020icpc亚洲区域赛徐州站H. Yuuki and a problem

    2019~2020icpc亚洲区域赛徐州站H. Yuuki and a problem 题意: 给定一个长度为\(n\)的序列,有两种操作: 1:单点修改. 2:查询区间\([L,R]\)范围内所有子 ...

  4. 2016ACM/ICPC亚洲区沈阳站 - A/B/C/E/G/H/I - (Undone)

    链接:传送门 A - Thickest Burger - [签到水题] ACM ICPC is launching a thick burger. The thickness (or the heig ...

  5. 2016ACM/ICPC亚洲区沈阳站H - Guessing the Dice Roll HDU - 5955 ac自动机+概率dp+高斯消元

    http://acm.hdu.edu.cn/showproblem.php?pid=5955 题意:给你长度为l的n组数,每个数1-6,每次扔色子,问你每个串第一次被匹配的概率是多少 题解:先建成ac ...

  6. 2019沈阳icpc网络赛H德州扑克

    题面:https://nanti.jisuanke.com/t/41408 题意:A,2,3,4,5,6,7,8,9,10,J,Q,K,13张牌,无花色之分,val为1~13. 给n个人名+n个牌,输 ...

  7. 2016ACM/ICPC亚洲区沈阳站-重现赛赛题

    今天做的沈阳站重现赛,自己还是太水,只做出两道签到题,另外两道看懂题意了,但是也没能做出来. 1. Thickest Burger Time Limit: 2000/1000 MS (Java/Oth ...

  8. HDU 5950 Recursive sequence 【递推+矩阵快速幂】 (2016ACM/ICPC亚洲区沈阳站)

    Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Other ...

  9. HDU 5952 Counting Cliques 【DFS+剪枝】 (2016ACM/ICPC亚洲区沈阳站)

    Counting Cliques Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  10. HDU 5948 Thickest Burger 【模拟】 (2016ACM/ICPC亚洲区沈阳站)

    Thickest Burger Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

随机推荐

  1. PRIx64:uint64_t类型输出为十六进制格式

    #include <stdio.h> #include <stdint.h> #include <inttypes.h> int main(void) { uint ...

  2. [图像处理] YUV图像处理入门2

    1 分离YUV420中YUV分量 本程序中的函数主要是将YUV420P视频数据流的第一帧图像中的Y.U.V三个分量分离开并保存成三个文件.函数的代码如下所示: /** * @file 1 yuv_sp ...

  3. 那些年我们用过的组件-结构化日志组件 Serilog

    什么是结构化日志 我们记录日志惯常使用 log4j2.NLog 等日志组件,这些组件提供了输出到多种终端的能力,但是大部分时候我们选择将日志输出到操作系统的文件系统中,为什么呢?至少有一部分原因是记录 ...

  4. 多项式 I:拉格朗日插值与快速傅里叶变换

    1. 复数和单位根 前置知识:弧度制,三角函数. 1.1 复数的引入 跳出实数域 \(\mathbb R\),我们定义 \(i ^ 2 = -1\),即 \(i = \sqrt {-1}\),并在此基 ...

  5. 从开发属于你自己的第一个 Python 库,做一名真正的程序员「双语版」

    你好,我是悦创.之前我在 CSDN 编写了一篇开发 Python 库的教程,有人加我提问到的一些问题,我来更新一下这篇文章:https://blog.csdn.net/qq_33254766/arti ...

  6. Linux c 检测U盘挂载路径方法

    思路: 1.使用df -h |grep mnt shell 命令查找到挂载路径信息 本代码是将结果存入文件中,再从文件中解析出路径信息.也可使用fopen直接从管道中读取信息 2.解析出信息最后的/m ...

  7. day16-声明式事务-02

    声明式事务-02 3.事务的传播机制 事务的传播机制说明: 当有多个事务处理并存时,如何控制? 比如用户去购买两次商品(使用不同的方法),每个方法都是一个事务,那么如何控制呢? 也就是说,某个方法本身 ...

  8. 【Oculus Interaction SDK】(六)实体按钮 && 按压交互

    前言 这篇文章是[Oculus Interaction SDK]系列的一部分,如果发现有对不上的对方,可以回去翻看我之前发布的文章,或在评论区留言.如果文章的内容已经不适用于新版本了,也可以直接联系我 ...

  9. 洛谷p2669

    #include<bits/stdc++.h> using namespace std; int main() { int k,m=0,p=1;//p:给j个金币的第p天(1~j循环变化) ...

  10. wsl2 使用串口/usb

    wsl2串口 搜了几个方案,都不合适 网上很多人说wsl可以直接使用win的串口,com* 就对应 /dev/ttyS* 实际上这是wsl1的,wsl2官方明确表示不支持了 CH340 介绍 CH34 ...