NC14844 codeJan与旅行

题目地址:

https://ac.nowcoder.com/acm/problem/14844

基本思路:

基本思路就是贪心,关键点是我们不走回头路,也就是说我们要么一直往左走,要么一直往右走,然后在每个位置我们都有可能开始左右横跳来回重复走。(这部分可以看其他大佬的证明,或者自己脑补证明一下,这里就不长篇幅证明了QAQ)。因为开始重复走之后需要走的距离是能O(1)立马算到的,所以我们从左右两边枚举,然后将在每个位置开始横跳得到的最终答案都算出来,然后取最小值就是了。

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF (int)1e18

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int maxn = 1e5 + 10;
int n,m,p;
int a[maxn];
int solve(int now){
  int ans = INF,sum = abs(a[now] - p) ,z = m - 1;
  if(z == 0) return sum;
  //一直往右走的情况;
  for(int i = now + 1; i <= n && z > 0; i++,z--){
    int tmp = abs(a[i] - a[i-1]);
    ans = min(ans,tmp * z + sum);//从这个位置开始重复来回走,直到走完;
    sum += tmp;//这里不开始横跳,就要把这部分走的距离加上;
  }
  sum = abs(a[now] - p) ,z = m - 1;
  //一直往左走的情况;
  for(int i = now - 1; i >= 1 && z > 0;i--,z--){
    int tmp = abs(a[i+1] - a[i]);
    ans = min(ans,tmp * z + sum);//从这个位置开始重复来回走,直到走完;
    sum += tmp;//这里不开始横跳,就要把这部分走的距离加上;
  }
  return ans;
}
signed main() {
  IO;
  int t;
  cin >> t;
  while(t--){
    cin >> n >> m >> p;
    rep(i,1,n) cin >> a[i];
    int now = lower_bound(a+1,a+1+n,p) - a;
    //可以从初始位置左边或右边的城市开始,所以两种情况都要算;
    int ans = solve(now);
    if(now - 1 >= 1) now--;
    ans = min(ans,solve(now));
    cout << ans << '\n';
  }
  return 0;
}