返回介绍

solution / 2900-2999 / 2969.Minimum Number of Coins for Fruits II / README_EN

发布于 2024-06-17 01:02:58 字数 9856 浏览 0 评论 0 收藏 0

2969. Minimum Number of Coins for Fruits II

中文文档

Description

You are at a fruit market with different types of exotic fruits on display.

You are given a 1-indexed array prices, where prices[i] denotes the number of coins needed to purchase the ith fruit.

The fruit market has the following offer:

  • If you purchase the ith fruit at prices[i] coins, you can get the next i fruits for free.

Note that even if you can take fruit j for free, you can still purchase it for prices[j] coins to receive a new offer.

Return _the minimum number of coins needed to acquire all the fruits_.

 

Example 1:

Input: prices = [3,1,2]
Output: 4
Explanation: You can acquire the fruits as follows:
- Purchase the 1st fruit with 3 coins, and you are allowed to take the 2nd fruit for free.
- Purchase the 2nd fruit with 1 coin, and you are allowed to take the 3rd fruit for free.
- Take the 3rd fruit for free.
Note that even though you were allowed to take the 2nd fruit for free, you purchased it because it is more optimal.
It can be proven that 4 is the minimum number of coins needed to acquire all the fruits.

Example 2:

Input: prices = [1,10,1,1]
Output: 2
Explanation: You can acquire the fruits as follows:
- Purchase the 1st fruit with 1 coin, and you are allowed to take the 2nd fruit for free.
- Take the 2nd fruit for free.
- Purchase the 3rd fruit for 1 coin, and you are allowed to take the 4th fruit for free.
- Take the 4th fruit for free.
It can be proven that 2 is the minimum number of coins needed to acquire all the fruits.

 

Constraints:

  • 1 <= prices.length <= 105
  • 1 <= prices[i] <= 105

Solutions

Solution 1: Dynamic Programming

We define $f[i]$ as the minimum number of coins needed to buy all fruits starting from the $i$th fruit. So the answer is $f[1]$.

The state transition equation is $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + prices[i - 1]$.

In implementation, we calculate from back to front, and we can directly perform state transition on the array $prices$, which can save space.

The time complexity of the above method is $O(n^2)$. Since the scale of $n$ in this problem reaches $10^5$, it will time out.

We observe the state transition equation and find that for each $i$, we need to find the minimum value of $f[i + 1], f[i + 2], \cdots, f[2i + 1]$, and as $i$ decreases, the range of these values is also decreasing. This is actually finding the minimum value of a monotonically narrowing sliding window, and we can use a monotonic queue to optimize.

We calculate from back to front, maintain a monotonically increasing queue $q$, and the queue stores the index. If the head element of $q$ is greater than $i \times 2 + 1$, it means that the elements after $i$ will not be used, so we dequeue the head element. If $i$ is not greater than $(n - 1) / 2$, then we can add $prices[q[0] - 1]$ to $prices[i - 1]$, and then add $i$ to the tail of the queue. If the price of the fruit corresponding to the tail element of $q$ is greater than or equal to $prices[i - 1]$, then we dequeue the tail element until the price of the fruit corresponding to the tail element is less than $prices[i - 1]$ or the queue is empty, and then add $i$ to the tail of the queue.

The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the length of the array $prices$.

class Solution:
  def minimumCoins(self, prices: List[int]) -> int:
    n = len(prices)
    q = deque()
    for i in range(n, 0, -1):
      while q and q[0] > i * 2 + 1:
        q.popleft()
      if i <= (n - 1) // 2:
        prices[i - 1] += prices[q[0] - 1]
      while q and prices[q[-1] - 1] >= prices[i - 1]:
        q.pop()
      q.append(i)
    return prices[0]
class Solution {
  public int minimumCoins(int[] prices) {
    int n = prices.length;
    Deque<Integer> q = new ArrayDeque<>();
    for (int i = n; i > 0; --i) {
      while (!q.isEmpty() && q.peek() > i * 2 + 1) {
        q.poll();
      }
      if (i <= (n - 1) / 2) {
        prices[i - 1] += prices[q.peek() - 1];
      }
      while (!q.isEmpty() && prices[q.peekLast() - 1] >= prices[i - 1]) {
        q.pollLast();
      }
      q.offer(i);
    }
    return prices[0];
  }
}
class Solution {
public:
  int minimumCoins(vector<int>& prices) {
    int n = prices.size();
    deque<int> q;
    for (int i = n; i; --i) {
      while (q.size() && q.front() > i * 2 + 1) {
        q.pop_front();
      }
      if (i <= (n - 1) / 2) {
        prices[i - 1] += prices[q.front() - 1];
      }
      while (q.size() && prices[q.back() - 1] >= prices[i - 1]) {
        q.pop_back();
      }
      q.push_back(i);
    }
    return prices[0];
  }
};
func minimumCoins(prices []int) int {
  n := len(prices)
  q := Deque{}
  for i := n; i > 0; i-- {
    for q.Size() > 0 && q.Front() > i*2+1 {
      q.PopFront()
    }
    if i <= (n-1)/2 {
      prices[i-1] += prices[q.Front()-1]
    }
    for q.Size() > 0 && prices[q.Back()-1] >= prices[i-1] {
      q.PopBack()
    }
    q.PushBack(i)
  }
  return prices[0]
}

// template
type Deque struct{ l, r []int }

func (q Deque) Empty() bool {
  return len(q.l) == 0 && len(q.r) == 0
}

func (q Deque) Size() int {
  return len(q.l) + len(q.r)
}

func (q *Deque) PushFront(v int) {
  q.l = append(q.l, v)
}

func (q *Deque) PushBack(v int) {
  q.r = append(q.r, v)
}

func (q *Deque) PopFront() (v int) {
  if len(q.l) > 0 {
    q.l, v = q.l[:len(q.l)-1], q.l[len(q.l)-1]
  } else {
    v, q.r = q.r[0], q.r[1:]
  }
  return
}

func (q *Deque) PopBack() (v int) {
  if len(q.r) > 0 {
    q.r, v = q.r[:len(q.r)-1], q.r[len(q.r)-1]
  } else {
    v, q.l = q.l[0], q.l[1:]
  }
  return
}

func (q Deque) Front() int {
  if len(q.l) > 0 {
    return q.l[len(q.l)-1]
  }
  return q.r[0]
}

func (q Deque) Back() int {
  if len(q.r) > 0 {
    return q.r[len(q.r)-1]
  }
  return q.l[0]
}

func (q Deque) Get(i int) int {
  if i < len(q.l) {
    return q.l[len(q.l)-1-i]
  }
  return q.r[i-len(q.l)]
}
function minimumCoins(prices: number[]): number {
  const n = prices.length;
  const q = new Deque<number>();
  for (let i = n; i; --i) {
    while (q.getSize() && q.frontValue()! > i * 2 + 1) {
      q.popFront();
    }
    if (i <= (n - 1) >> 1) {
      prices[i - 1] += prices[q.frontValue()! - 1];
    }
    while (q.getSize() && prices[q.backValue()! - 1] >= prices[i - 1]) {
      q.popBack();
    }
    q.pushBack(i);
  }
  return prices[0];
}

class Node<T> {
  value: T;
  next: Node<T> | null;
  prev: Node<T> | null;

  constructor(value: T) {
    this.value = value;
    this.next = null;
    this.prev = null;
  }
}

class Deque<T> {
  private front: Node<T> | null;
  private back: Node<T> | null;
  private size: number;

  constructor() {
    this.front = null;
    this.back = null;
    this.size = 0;
  }

  pushFront(val: T): void {
    const newNode = new Node(val);
    if (this.isEmpty()) {
      this.front = newNode;
      this.back = newNode;
    } else {
      newNode.next = this.front;
      this.front!.prev = newNode;
      this.front = newNode;
    }
    this.size++;
  }

  pushBack(val: T): void {
    const newNode = new Node(val);
    if (this.isEmpty()) {
      this.front = newNode;
      this.back = newNode;
    } else {
      newNode.prev = this.back;
      this.back!.next = newNode;
      this.back = newNode;
    }
    this.size++;
  }

  popFront(): T | undefined {
    if (this.isEmpty()) {
      return undefined;
    }
    const value = this.front!.value;
    this.front = this.front!.next;
    if (this.front !== null) {
      this.front.prev = null;
    } else {
      this.back = null;
    }
    this.size--;
    return value;
  }

  popBack(): T | undefined {
    if (this.isEmpty()) {
      return undefined;
    }
    const value = this.back!.value;
    this.back = this.back!.prev;
    if (this.back !== null) {
      this.back.next = null;
    } else {
      this.front = null;
    }
    this.size--;
    return value;
  }

  frontValue(): T | undefined {
    return this.front?.value;
  }

  backValue(): T | undefined {
    return this.back?.value;
  }

  getSize(): number {
    return this.size;
  }

  isEmpty(): boolean {
    return this.size === 0;
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文