LGR-084 C Reboot from Blue 贪心 DAG上DP最短路
题意
数轴上有(n)个加油站,第(i)个位于(x_i),油价为每升(c_i)元
起点坐标(s),终点(t),车油箱一开始是空的,且保证起点处有加油站,假设箱子容量无限大,一升油可以走距离1
求出最小花费
[1 leq n leq 10^5\
-10^9 leq x_i,s,t leq 10^9,s < t
]
分析
容易想到此题是 51nod1288 的改编
我们肯定贪心地每次走到下一个价格比当前位置小的位置加油,这一性质就可以用单调栈维护,有考虑到这题由往回走的可能性,所以需要一定转化。
对于当前点和左右第一个小于当前点的点连边,边权为距离乘油价,容易知道这样可以构成一张DAG,因此最后只需要在DAG上跑最短路即可,这可以用拓扑排序DP实现。
注意点:1.起点应该保证入度为0,否则无法拓扑排序 2.单调栈实现时,必须找严格小于的位置,否则会形成环。 3.拓扑排序时,一开始必须把所有入度为0的点加入队列,否则可能导致一些点入度始终不能到0的情况
代码
#include<bits/stdc++.h>
#define pii pair<int,ll>
#define fi first
#define se second
#define re register
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
const int maxn = 1e5 + 5;
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
struct Edge{
int v;
ll w;
Edge(int _v,ll _w) :v(_v),w(_w) {}
};
pii p[maxn];
ll dp[maxn];
vector<Edge> e[maxn];
int in[maxn];
int main(){
int n = rd();
int s = rd();
int t = rd();
for(int i = 1;i <= n;i++){
p[i].se = rd();
p[i].fi = rd();
}
p[++n] = make_pair(t,0);
sort(p + 1,p + n + 1);
int ss,tt;
ss = tt = 1;
for(int i = 1;i <= n;i++)
if(p[i].fi == s) ss = i;
else if(p[i].fi == t && !p[i].se) tt = i;
stack<int> st;
for(int i = 1;i <= n;i++){
while(!st.empty() && p[i].se <= p[st.top()].se) {
st.pop();
}
if(!st.empty()) e[i].push_back(Edge(st.top(),p[i].se * (p[i].fi - p[st.top()].fi))),in[st.top()]++;
if(i ^ ss)
st.push(i);
}
while(!st.empty()) st.pop();
for(int i = n;i >= 1;i--){
while(!st.empty() && p[i].se <= p[st.top()].se) {
st.pop();
}
if(!st.empty()) e[i].push_back(Edge(st.top(),-1 * p[i].se * (p[i].fi - p[st.top()].fi))),in[st.top()]++;
if(i ^ ss)
st.push(i);
}
queue<int> q;
for(int i = 1;i <= n;i++)
if(!in[i]) q.push(i);
for(int i = 1;i <= n;i++)
dp[i] = 5e18;
dp[ss] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for(auto v:e[u]) {
if(dp[v.v] > dp[u] + v.w)
dp[v.v] = dp[u] + v.w;
if(!--in[v.v])
q.push(v.v);
}
}
cout << dp[tt];
}