Friday, August 5, 2016

Burger Happiness - Hacker Rank Solution

In Burger Town new burger restaurants will be opened! Concretely,  restaurants will open in  days, while restaurant  will be opened on day  and will be located at . The town should be imagined as an one dimensional line in which every object's location can be described by the  coordinate.
Tim has just recently arrived the town after a very bad result in a programming contest. Thus he wants to cheer himself up by starting a trip to try out some new burgers.
Every burger restaurant  is associated with two integers  and . If Tim eats a burger from , then his happiness will increase by , which can also be negative, depending on the deliciousness of the burger. On the other hand, if Tim looks through the window of an opened restaurant , from which he will not eat a burger, then his happiness decreases by , since Tim gets sad by only seeing the burgers.
Tim's journey can start from any day  at the burger restaurant  and eats a burger from there. On each subsequent day , Tim has the following options:
  • Stay at the previous restaurant .
  • Or go to the new restaurant  to eat a burger from there.
If he decides for the latter option, then on the path from  to  he will look through all the windows that are on his path and maybe lose some happiness. Concretely, if , then he will look through the window of everyopened restaurant , having . Similar for the case .
Since Tim is a very good friend of yours you should help him finding a trip that will maximize his happiness. If he should stay at home since no trip would cheer him up, then print 0.
Note: Tim's happiness is 0 at the beginning of the trip and is allowed to be negative throughout the time.
Input Format
 will be given on the first line, then  lines will follow, describing the restaurants numbered from 1 to accordingly. Restaurant  will be described by  and  separated by a single space.
Output Format
Output the maximium happiness on one line.
Constraints
  •  and no two restaurants will have the same  coordinates.
Sample Input
 3
 2 -5 1
 1 5 1
 3 5 1
Sample Output
8
Sample Input
 4
 4 10 0
 1 -5 0
 3 0 10
 2 10 0
Sample Output
 15
Sample Input
 3
 1 -1 0
 2 -2 0
 3 -3 0
Sample Output
 0
First testcase: His trip starts on day 2 at restaurant 2 located at . He gains  happiness points there by eating a burger. On the next day he goes from restaurant 2 to 3, but will look through the window of restaurant 2 and 1. Therefore he loses  and  points on the way to restaurant 3. There he eats a burger and gains another  points. In total his happiness is equal to  8 and this is optimal.
Second testcase: His trip starts on day 1 at restaurant 1. Then his actions on day 2, 3 and 4 will be go to restaurant 2, stay at restaurant 2 and go to restaurant 4 respectively. The happiness of this optimal trip is equal to  15.
Third testcase: It's not worth to start the trip from any of the restaurant since he will only have negative happiness. That's why he should stay at home and 0 should be printed.
------------------------------------------------------------------------------------------------

Burger Happiness - Hacker Rank Solution

There exists a simple DP recurrence for which we can obtain an  solution first. We will optimize this later with a data structure, but let's begin with the slower ones first. Many good solutions in CP always start from slow and simple ideas and get optimized with clever thinking.
Since every restaurant has one unique  coordinate, we can redefine our denotation and say that the values and  are for the restaurant located at .
We will introduce  which means:  is the maximum happiness we can get by ending our tour at restaurant located at .
The recurrence can be derived as  where  is the location of the previous restaurant that is also opened. Our solution will be then the maximum over all  is the happiness we lose on the path from  to , which is also equal to the sum of 's of the opened restaurants when we move along from  to .
OK, that's an  algorithm.
As we might know, the sum of B's in an interval  can be calculated with a very well known method. Since the sum between  is equal to the sum between  subtracted by the sum between  we can somehow maintain the prefix sums dynamically with a Fenwick Tree or a Segment Tree.
Let's denote  for all opened restaurants .
Consider the case  in our DP recurrence again, but now with more handy definitions:
The value we need to maximize is only the term in the  function. This actually means, we need to find in the interval  an element  with the maximum  value.
Now do the case  as an exercise, to find out which value we need to maximize for searching in the interval .
OK, this slowly starts to sound like a task involving RMQ (range maximum query) with segment tree, since we need to do range queries all the time to find the optimal values. Actually, if you figure out both cases, you will realize that we need to maintain two segment trees, since the values to maximize for both cases aren't the same.
Every time when we calculate our , we will add the corresponding values to our two segment trees at position so that future  values can use this  as an option for maximizing their  values.
But wait,  can be changing througout the time! No problem, if a burger restaurant opens at , it will affect all  values that have coordinates greater than . Concretely, all elements in the interval  will add  to their  value. This range updating can be done by using lazy propagation.
In total we will need two  queries for each  value to calculate. In total we will have time complexity of.
Hint: Coordinate compression on  actually simplifies the implementation. :-)

Problem Setter's code :
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;
typedef long long ll;
const int MAX_N = 1e5;
const ll INF = 1LL << 62;
const int MAX_A = 1e6;
const int MAX_X = 1e9;
const int MAX_B = 1e6;

int N;
int X[MAX_N], A[MAX_N], B[MAX_N]; // Location, Gain, Lose
ll F[MAX_N];

int tmp[MAX_N];
struct SegmentTree {
  ll lazy[MAX_N * 4];
  ll T[MAX_N * 4];
  void init(){
    memset(lazy, 0, sizeof lazy);
    memset(T, 0, sizeof T);
  }
  void propagate(int n, int L, int R){
    T[n] += lazy[n];
    if(L != R){
      lazy[n * 2] += lazy[n];
      lazy[n * 2 + 1] += lazy[n];
    }
    lazy[n] = 0;
  }

  void update(int n, int L, int R, int i, int j, ll x){
    propagate(n, L, R);
    if(R < i || j < L) return;
    if(i <= L && R <= j){
      lazy[n] = x;
      propagate(n, L, R);
    } else {
      update(n * 2, L, (L + R) / 2, i, j, x);
      update(n * 2 + 1, (L + R) / 2 + 1, R, i, j, x);
      T[n] = max(T[n * 2], T[n * 2 + 1]);
    }
  }

  void update(int i, int j, ll x){
    if(i <= j)
      update(1, 0, N - 1, i, j, x);
  }

  ll query(int n, int L, int R, int i, int j){
    if(R < i || j < L) return -INF;
    propagate(n, L, R);
    if(i <= L && R <= j) return T[n];
    return max(query(n * 2, L, (L + R) / 2, i, j),
           query(n * 2 + 1, (L + R) / 2 + 1, R, i, j));
  }
  ll query(int i, int j){
    if(i > j) return -INF;
    return query(1, 0, N - 1, i, j);
  }
};

SegmentTree T1; // Stores maximum f(x') + S[x' - 1]
SegmentTree T2; // Stores maximum f(x') - S[x']

ll query(int x, int a){
  ll S = -T2.query(x, x);  // S[x], since F[x] = 0
  ll S1 = T1.query(x, x);  // S[x - 1], since F[x] = 0
  // Case x' < x
  ll res1 = -S + a + T1.query(0, x - 1);
  // Case x < x'
  ll res2 = S1 + a + T2.query(x + 1, N - 1);
  // Case Beginning from x
  ll res3 = a;
  return max(max(res1, res2), res3);
}

void update(int x, int b){
  T1.update(x, x, F[x]);
  T1.update(x + 1, N - 1, +b);

  T2.update(x, x, F[x]);
  T2.update(x, N - 1, -b);
}

int main(){
  T1.init(), T2.init();
  scanf("%d", &N);
  assert(1 <= N && N <= MAX_N);
  for(int i = 0; i < N; i++){
    scanf("%d%d%d", X + i, A + i, B + i);
    assert(0 <= X[i] && X[i] <= MAX_X);
    assert(0 <= B[i] <= MAX_B);
    assert(-MAX_A <= A[i] && A[i] <= MAX_A);
    tmp[i] = X[i];
  }
  sort(tmp, tmp + N);
  int m = unique(tmp, tmp + N) - tmp;
  for(int i = 0; i < N; i++){
    X[i] = lower_bound(tmp, tmp + m, X[i]) - tmp;
  }
  ll res = 0;
  for(int i = 0; i < N; i++){
    F[X[i]] = query(X[i], A[i]);
    update(X[i], B[i]);
    res = max(res, F[X[i]]);
  }
  printf("%lld\n", res);
  return 0;
}

No comments:

Post a Comment

Powered by Blogger.