Farmer John's cows NN are very particular about the room temperature in their barn. Some cows like the temperature to be on the cooler side, while others prefer more warmth.
Farmer John's barn contains a sequence of NN stalls, numbered 1…N1…N, each containing a single cow. The ii-th cow prefers the temperature of her stall to be pipi, and right now the temperature in her stall is titi. In order to make sure every cow is comfortable, Farmer John installs a new air conditioning system that is controlled in a somewhat interesting way. He can send commands to the system telling it to either raise or lower the temperature in a consecutive series of stalls by 1 unit --- for example "raise the temperature in stalls 5…85…8 by 1 unit". The series of stalls could be as short as just a single stall.
Please help Farmer John determine the minimum number of commands he needs to send his new air conditioning system so that every cow's stall is at the ideal temperature for its resident cow.
The first line of input contains NN. The next line contains the NN non-negative integers p1…pNp1…pN, separated by spaces. The final line contains the NN non-negative integers t1…tNt1…tN.
Please write a single integer as output containing the minimum number of commands Farmer John needs to use.
5 1 5 3 3 4 1 2 2 2 1
5 One optimal set of commands Farmer John can use might be the following:
Initial temperatures: 1 2 2 2 1 Increase stalls 2..5: 1 3 3 3 2 Increase stalls 2..5: 1 4 4 4 3 Increase stalls 2..5: 1 5 5 5 4 Decrease stalls 3..4: 1 5 4 4 4 Decrease stalls 3..4: 1 5 3 3 4
Test cases 2-5 satisfy N≤100N≤100.
Test cases 6-8 satisfy N≤1000N≤1000.
Test cases 9-10 satisfy N≤100,000N≤100,000.
In test cases 1-6 and 9, temperature values are at most 100100.
In test cases 7-8 and 10, temperature values are at most 10,00010,000.
Problem credits: Brian Dean
(Analysis by Benjamin Qi and Nick Wu)
We'll start by defining di=pi−tidi=pi−ti for all ii in 1…N1…N. Note that didi is therefore the amount the temperature needs to change for cow ii to be happy. Now, instead of making pi=tipi=ti, we can focus on making didi zero. Note that, just as we can increase or decrease all values in some subsegment of tt by 1, we can increase or decrease all values in some subsegment of dd by 11.
How do we make dd zero everywhere making as few changes as possible? Intuitively, we want to avoid increasing values of dd that are already positive or decreasing values of dd that are already negative. We also don't want to touch values of dd that are zero.
One strategy that we might try therefore is as follows - assuming that dNdN is positive, find the smallest jj such that djdj through dNdN are all positive, and then decrease all those numbers by one. If dNdN is negative, we apply similar logic except we increase as many negative numbers as possible by one. In other words, find the longest suffix where all numbers are positive or all numbers are negative, and then adjust all of them towards zero.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.StringTokenizer; public class Solution { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); ArrayList<Integer> d = new ArrayList<>(); { int n = Integer.parseInt(in.readLine()); int[] p = new int[n]; StringTokenizer st = new StringTokenizer(in.readLine()); for(int i = 0; i < n; i++) p[i] = Integer.parseInt(st.nextToken()); int[] t = new int[n]; st = new StringTokenizer(in.readLine()); for(int i = 0; i < n; i++) t[i] = Integer.parseInt(st.nextToken()); for(int i = 0; i < n; i++) d.add(p[i] - t[i]); } int ans = 0; while(!d.isEmpty()) { if(d.get(d.size()-1) == 0) { d.remove(d.size()-1); continue; } boolean positive = d.get(d.size()-1) > 0; int amtChange = 1; while(amtChange < d.size()) { if(d.get(d.size()-1-amtChange) == 0) break; if((d.get(d.size()-1-amtChange) > 0) != positive) break; amtChange++; } ans++; for(int i = 0; i < amtChange; i++) { if(d.get(d.size()-1-i) > 0) { d.set(d.size()-1-i, d.get(d.size()-1-i) - 1); } else { d.set(d.size()-1-i, d.get(d.size()-1-i) + 1); } } } System.out.println(ans); } }
Ben's code:
#include <bits/stdc++.h> using namespace std; int main() { int N; cin >> N; vector<int> p(N), t(N), d(N); for (int i = 0; i < N; ++i) cin >> p[i]; for (int i = 0; i < N; ++i) cin >> t[i]; for (int i = 0; i < N; ++i) d[i] = p[i] - t[i]; int first_nonzero = 0, ans = 0; while (true) { while (first_nonzero < N && d[first_nonzero] == 0) ++first_nonzero; if (first_nonzero == N) break; int r = first_nonzero; auto sgn = [&](int x) { if (x < 0) return -1; if (x > 0) return 1; return 0; }; while (r + 1 < N && sgn(d[r + 1]) == sgn(d[first_nonzero])) ++r; for (int i = first_nonzero; i <= r; ++i) { if (d[i] < 0) ++d[i]; else --d[i]; } ++ans; } cout << ans << "\n"; }
These two solutions are O(N⋅V)O(N⋅V), where VV is the maximum possible value in dd. Under the given bounds though, the answer can be as large as one billion, so we need to do better than simulating this step by step.
One thing worth trying that does pass all test cases is, instead of just incrementing or decrementing by one, doing as many increments/decrements as possible until some element becomes zero.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.StringTokenizer; public class Solution { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); ArrayList<Integer> d = new ArrayList<>(); { int n = Integer.parseInt(in.readLine()); int[] p = new int[n]; StringTokenizer st = new StringTokenizer(in.readLine()); for(int i = 0; i < n; i++) p[i] = Integer.parseInt(st.nextToken()); int[] t = new int[n]; st = new StringTokenizer(in.readLine()); for(int i = 0; i < n; i++) t[i] = Integer.parseInt(st.nextToken()); for(int i = 0; i < n; i++) d.add(p[i] - t[i]); } int ans = 0; while(!d.isEmpty()) { if(d.get(d.size()-1) == 0) { d.remove(d.size()-1); continue; } boolean positive = d.get(d.size()-1) > 0; int amtChange = 1; int delta = Math.abs(d.get(d.size()-1)); while(amtChange < d.size()) { if(d.get(d.size()-1-amtChange) == 0) break; if((d.get(d.size()-1-amtChange) > 0) != positive) break; delta = Math.min(delta, Math.abs(d.get(d.size()-1-amtChange))); amtChange++; } ans += delta; for(int i = 0; i < amtChange; i++) { if(d.get(d.size()-1-i) > 0) { d.set(d.size()-1-i, d.get(d.size()-1-i) - delta); } else { d.set(d.size()-1-i, d.get(d.size()-1-i) + delta); } } } System.out.println(ans); } }
This code also runs in O(N⋅V)O(N⋅V), but it does significantly better on test cases where the answer is large compared to the first two solutions.
We can do provably better though - in particular, we can solve this problem in O(N)O(N).
We'll add a zero to the beginning and end of dd - specifically, we'll define d0=dN+1=0d0=dN+1=0. This does not change the answer, as we never need to change any zeroes. We'll also define ei=|di+1−di|ei=|di+1−di| - that is, the difference between adjacent values of didi. Why is eiei important? If eiei is zero, then didi and di+1di+1 are the same and any operation we do to didi should also be done to di+1di+1. However, if eiei is large, then the two values are very different, and there must be at least eiei operations that take place on one of didi and di+1di+1 but not the other.
More specifically, when we increase the range of values didi through djdj, note that ei−1ei−1 and ejej change by one each, and all other values in ee remain unchanged. This motivates the following claim.
Claim: The answer is ∑Ni=0ei2∑i=0Nei2, or half the sum of all the values in ee.
Proof: The sum of all values of ee equals zero if and only if all didi are zero. Furthermore, every command changes this quantity by at most 22. This shows that the answer is at least ∑Ni=0ei2∑i=0Nei2.
To show that this answer is attainable, any number of greedy strategies suffice. One valid strategy is the one we had above - find the longest suffix of all positive or all negative integers, and adjust them towards zero by one.
We can evaluate the formula mentioned above in O(N)O(N) time. Ben's code:
#include <bits/stdc++.h> using namespace std; int main() { int N; cin >> N; vector<int> p(N + 1), t(N + 1), d(N + 2); for (int i = 1; i <= N; ++i) cin >> p[i]; for (int i = 1; i <= N; ++i) cin >> t[i]; for (int i = 1; i <= N; ++i) d[i] = p[i] - t[i]; int ans = 0; for (int i = 0; i <= N; ++i) ans += abs(d[i] - d[i + 1]); cout << ans / 2; }
In Python:
N = int(input()) P = list(map(int,input().split())) T = list(map(int,input().split())) difs = [x-y for x,y in zip(P,T)] print(sum(abs(x-y) for x,y in zip(difs+[0],[0]+difs))//2)
翰林课程体验,退费流程快速投诉邮箱: yuxi@linstitute.net 沪ICP备2023009024号-1