博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
「BZOJ2153」设计铁路 - 斜率DP
阅读量:4316 次
发布时间:2019-06-06

本文共 2694 字,大约阅读时间需要 8 分钟。

A省有一条东西向的公路经常堵车,为解决这一问题,省政府对此展开了调查。

调查后得知,这条公路两侧有很多村落,每个村落里都住着很多个信仰c教的教徒,每周日都会开着自家的车沿公路到B地去“膜拜”他们的教主,这便是堵车的原因。

详细调查显示:这里总共有N个村落,并且它们都在B地的东边。编号为i的村落住有Ri个信仰c教的教徒,距离B地的距离为Ti(单位:公里)。为解决这一问题,A省政府决定在这条公路下修建一条地下快速铁路来缓解交通,并沿线修建若干个车站(B地会修建终点站,不算车站)。

每名教徒都会先往B地方向开车(如果他所在村庄处恰好有车站就不必开车了),到最近的一个快速铁路车站时换乘(如果直接开到B地就不用换乘了),再通过快速铁路到B地。

但A政府遇到一个难题:修建多少个车站以及在哪修建车站。一个修建车站的方案中,如果修建过多的车站则会花费过多的钱,但修建的车站少了或者修建的位置不对又会导致公路的拥堵。

A政府为了协调这两方面,采用评分的方式来衡量一个方案的好坏(分数越大方案越坏):每修建一个车站会增加m的分数,在某一次“膜拜”中(只考虑去,不考虑返回),每导致1个教徒开车行驶1公里会增加1分。

现请你设计一个修建车站的方案,使得分数最小。请输出这个最小的分数。

解题报告

首先考虑贪心是否可做,如果只能选取一个点设置车站,不妨设这个点距离为 \(x\),花费总和就是 \(\sum_{i=1}^n (T_i \times R_i) + m - x \times \sum_{i=1}^n[T_i \ge x]\) ,前面部分是常数,而后面部分是一个无法确定有几个峰的函数,因此我们需要考虑动态规划或者模拟退火解决这个问题

由于模拟退火的玄学性,这里不做多的讨论,可以尝试 \(O(n*(退火复杂度))\) 进行依次退火,下面我们将用动态规划解决本题

我们不妨将 \(T_i\) 按照降序排序,我们将会有一个比较原始的区间DP

我们用 \(f[i]\) 表示前 \(i\) 个村落建立若干车站的最小值,且 \(i\) 处有车站设立。则 :

\[f[i] = \min_{j \le i}\{f[j] + m+\sum_{j < k \le i}(R_k * (T_k - T_i))\}\]

利用前缀和,我们令 \(sumT[i]\) 表示 \(R[i]*T[i]\) 的前缀和,\(sum[i]\) 表示 \(R[i]\) 的前缀和,则:

\[f[i] = \min_{j \le i}\{f[j] + m+sumT[i] - sumT[j]-T_i*(sum[i]-sum[j])\}\]

此时这个问题可以在 \(O(n^2)\) 时间求解

观察这个式子,发现\(i\) 已经确定的时候 $f[i] = \min_{j \le i}{-sumT[j] + T_i * sum[j] + f[j]} + K $ 此处 \(K = m + sumT[i] - T_i * sum[i]\) 是一个常数,换句话说,我们只需要找出最小的 \(- sumT[j] + T_i * sum[j] + f[j]\) 即可

我们不妨考虑这样一条直线 \(y = - T_i * (x - sum[j]) + f[j] - sumT[j]\) 过点 \((sum[j], f[j] - sumT[j])\), 它的截距就是我们要求的 \(- sumT[j] + T_i * sum[j] + f[j]\) 要使截距最小,那直线就应该尽量往下移动

我们可以发现我们选取的最优点应该是在一个下凸壳上的,由于 \(sum[j]\) 时单调的,我们可以用一个简单的单调队列进行维护。

此时复杂度降为了 \(O(n)\)

代码

#include 
#define T first#define R secondtypedef long long ll;const int maxn = 40000 + 10;int n, q[maxn];ll m, s1[maxn], s2[maxn], f[maxn];std::pair
p[maxn];ll F(int i, int j) { return f[j] + s1[i] - s1[j] - p[i].T * (s2[i] - s2[j]);}double slope(int i, int j) { double x1 = s2[i], y1 = f[i] - s1[i]; double x2 = s2[j], y2 = f[j] - s1[j]; return (y2 - y1) / (x2 - x1);}int main() { scanf("%d %lld", &n, &m); for (int i = 1; i <= n; ++ i) { scanf("%d %d", &p[i].T, &p[i].R); } std::sort(p + 1, p + n + 1); std::reverse(p + 1, p + n + 1); p[++n] = std::make_pair(0, 0); for (int i = 1; i <= n; ++ i) { s1[i] = s1[i - 1] + p[i].T * p[i].R; s2[i] = s2[i - 1] + p[i].R; } int head = 1, tail = 1; q[1] = 0; for (int i = 1; i <= n; ++ i) { while (head < tail && F(i, q[head]) >= F(i, q[head + 1])) head ++; f[i] = F(i, q[head]); if (i != n) f[i] += m; while (head < tail && slope(i, q[tail]) < slope(i, q[tail - 1])) tail --; q[++tail] = i; } printf("%lld", f[n]);}

转载于:https://www.cnblogs.com/Alessandro/p/10095812.html

你可能感兴趣的文章
Activity的几种启动跳转方式
查看>>
LCA最近公共祖先Tarjan(离线)
查看>>
牛客练习赛16 E求值
查看>>
matlab rank
查看>>
Asp.net系列--基础篇(三)
查看>>
css基础
查看>>
如何在tomcat中如何部署java EE项目
查看>>
【Python基础教程第2版】——第二讲:列表和元组
查看>>
小常识
查看>>
使用vscode开发python
查看>>
swift--调用系统单例实现打电话
查看>>
0038-算一算是一年中的第几天
查看>>
51nod 1094 【水题】
查看>>
003.第一个动画:绘制直线
查看>>
ng-深度学习-课程笔记-2: 神经网络中的逻辑回归(Week2)
查看>>
正则表达式的搜索和替换
查看>>
个人项目:WC
查看>>
地鼠的困境SSL1333 最大匹配
查看>>
flume+elasticsearch+kibana遇到的坑
查看>>
【MM系列】在SAP里查看数据的方法
查看>>