NC14844 codeJan与旅行
题目地址:
基本思路:
基本思路就是贪心,关键点是我们不走回头路,也就是说我们要么一直往左走,要么一直往右走,然后在每个位置我们都有可能开始左右横跳来回重复走。(这部分可以看其他大佬的证明,或者自己脑补证明一下,这里就不长篇幅证明了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; }