【每日一题】5月8日codeJan与旅行
codeJan与旅行
https://ac.nowcoder.com/acm/problem/14844
解题思路
给定n个城市坐标,每个城市可以多次到达,但是只有离开再回来才算次数+1,问一共到m次,最短花费。给出起始位置,并且起始位置不在城市上。
首先通过强大的观察法我们得到最终一定是在两个城市之间反复横跳,或者一次向一个方向走到m个城市,这两种方案。
具体如何得到的:
- 假设走到当前节点跳到后面节点后,还往后面走,那么就会到我们下一次选择下一个点位终点反复横跳。
- 如果这时候我们选择回头,那么一定是就回头一个格子,在这个地方反复横跳,为什么,如果在前一个格子距离更短,那我为什么不在再前面一个格子反复横跳,要多跑两个格子再后面这个地方反复横跳。
- 那么同理,如果我选择2个以上点反复横跳,花费一定会大于2个点反复横跳的花费,因为你选择了花费大的,让到达城市数目加1。
那么结合我上面分析的几种情况,得到最终一定是在两个连续的节点之间反复横跳,或者一次都不回头。
那么在这里又有两种情况,为什么给组样例就懂了。
3 4 2
1 10 12
ans = 42
没错这个地方我们出发点,选择回退一格,再重新前进反复横跳,为什么因为我们在回退一个格子再回来出发点,花费小于反复横跳一次的花费。
当然要选择最优的答案,那么这个地方就要特判出发点前面是不是有格子了,不能再我给的代码思路里面随便拿索引去找。注意判断边界。
给出代码,注意记录在我出发点之前的第一个城市位置,再枚举反复横跳的城市。
#include <bits/stdc++.h> #pragma GCC optimize(2) #pragma GCC optimize(3) using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) typedef long long ll; inline int read() { int s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 1e5 + 7; ll a[N]; int main() { int T = read(); while (T--) { ll n = read(), m = read(), p = read(), st = 0; for (int i = 1; i <= n; ++i) { a[i] = read(); if (a[i] < p) st = i; //确保不站在city上 } ll ans = INF; for (int i = 1; i < n; ++i) { //i与i+1间反复横跳 if (i <= st) { if (st - i + 1 > m) continue; ll cnt = m - (st - i + 1), d = p - a[i]; ans = min(ans, (a[i + 1] - a[i]) * cnt + d); if (cnt && st != n) ans = min(ans, (a[st + 1] - p) * 2 + d + (cnt - 1) * (a[i + 1] - a[i])); } else { if (i - st > m) continue; ll cnt = m - (i - st), d = a[i] - p; ans = min(ans, d + cnt * (a[i + 1] - a[i])); if (cnt && st) //st可能为0 ans = min(ans, (p - a[st]) * 2 + d + (cnt - 1) * (a[i + 1] - a[i])); } } printf("%lld\n", ans); } return 0; }
每日一题 文章被收录于专栏
每日一题