"Algorithm Competition·Quick 300 Questions" One question per day: "Cow Coupon"

" Algorithm Competition: 300 Quick Questions " will be published in 2024 and is an auxiliary exercise book for "Algorithm Competition" .
All questions are placed in the self-built OJ New Online Judge .
Codes are given in three languages: C/C++, Java, and Python. The topics are mainly mid- to low-level topics and are suitable for entry-level and advanced students.


" Cow Coupon ", link: http://oj.ecustacm.cn/problem.php?id=1895

Question description

[Title description] Farmer John needs new cows!
There are currently N cows for sale, the farmer's budget is only M yuan, and cow i costs P_i.
But the farmer has K coupons. When using the coupon for cow i, he only needs to spend C_i (C_i <= P_i).
Only one coupon can be used per cow.
How many cows can a farmer raise at most?
[Input format] The first line contains three positive integers N, K, M, 1 <= N <= 50,000, 1 <= M <= 10^14, 1 <= K <= N.
Next N lines, each line contains two integers P_i and C_i, 1 <= P_i <= 10^9, 1 <= C_i <= P_i.
[Output format] Output an integer representing the answer.
【Input sample】

4 1 7
3 2
2 2
8 1
4 3

【Output sample】

3

answer

   The meaning of the question is briefly described as follows: There are n numbers, and each number can be replaced with a smaller number; select some from the n numbers, and you are allowed to replace up to k numbers when selecting. The sum of these numbers is required to be no greater than m. Question What is the maximum number that can be selected.
   This problem can be compared with the analogy of girls buying clothes. A girl goes shopping for clothes with m yuan, and her goal is to buy as many pieces as possible, the more, the better. Each item of clothing has a discount, but coupons must be used, and only one can be used per item of clothing. Discounts on clothes vary. It is possible that a product that was originally expensive will become cheaper after the discount. A girl has k coupons. Ask her how many pieces of clothing she can buy at most.
   Male readers can ask their girlfriend how she would buy clothes. If she is smart, she may immediately ask: Are there unlimited coupons? If the coupons cannot be used up, then the original price of the clothes is in vain, so just buy from the smallest to the largest discount prices. Unfortunately, there are never enough coupons.
   She came up with this method: sort by discount price, buy the cheaper ones first until the coupons are used up; if there is still money, buy the cheaper ones at the original price.
   However, this method is not optimal, because a low discount price may have a small discount margin, causing the coupon to be wasted. For example:
      Clothes: a, b, c, d, e.
      Discount price: 3, 4, 5, 6, 7.
      Original price: 4, 5, 6, 15, 10.
   There are m = 20 yuan and k = 3 coupons. It is not optimal to use 3 coupons on a, b, and c, so you can only buy 3 items. The optimal solution is to buy 4 pieces: a, b, d at the discounted price, and c at the original price, a total of 19 yuan.
   This method is improved below. Since there are clothes with huge discounts, try transferring the coupons to the clothes to see if you can get a bigger discount. Call this transfer a "repentance."
   Assume that the first k items with the cheapest discounts have used up k coupons. Now look at the i = k+1 piece of clothing, either buy it at the original price, or transfer a coupon to buy it at a discounted price, whichever is better. Suppose the original price is p and the preferential price is c.
   Before regretting it, the i-th item was bought at the original price pi, and the previous j-th item was bought at the discounted price cj. The total cost is:
      tot + pi + cj, where tot is the cost of other clothes that have been bought.
   After regretting, the j-th item transfers the coupon to the i-th item, changing it to the original price pj, and the i-th item uses the preferential price ci. The total cost is:
      tot + ci + pj.
   If it is better to regret, then there is:
      tot + pi + cj < tot + cj + pi,
   that is, pj - cj< pi - ci, assuming Δ= p - c, there is Δj <Δi, Δ is the difference between the original price and the preferential price.
   That is to say, as long as there is a j among the clothes using coupons, Δj <Δi, that is to say, the discount margin of the j-th item is not as good as the discount margin of the i-th item, then transferring j's coupon to i will have Better results.
   But there is a problem with the above discussion, which may lead to more than the total cost. For example:
      clothes: a, b, c
      discount price: 20, 40, 42
      original price: 30, 80, 49
   m = 69 yuan, k = 1 coupon. First use the coupon to buy a; next step is to find Δa <Δb, transfer the coupon to b, the current cost is 30 + 40 = 70, which exceeds m. The optimal solution is that a still uses the preferential price and c uses the original price. So simply calculating the difference Δi = pi - ci for candidate clothing i and then comparing it with Δj does not work.
   So how to choose the best deal among the candidate clothes?
   (1) If you buy at the original price, then it should be the lowest original price among these clothes;
   (2) If you buy at the discount price, then it should be the lowest discount price among these clothes.
   Therefore, it makes sense to calculate the difference between the lowest original price and the lowest discount price among the candidate clothes and compare it with Δj.
   When coding, use 3 priority queues to process 3 key data:
   (1) The discount margin Δ of clothes that have used coupons. Among the clothes that have already used coupons, who should take out and transfer the coupons? It should be the one with the smallest discount margin Δ, so that you can get a greater discount after the transfer. Use priority queue d to find the smallest Δ.
   (2) The original price of the clothes without coupons. Use a priority queue p to find the cheapest original price.
   (3) Discounted prices for clothes without coupons. Use a priority queue c to find the cheapest price.
   The code handles coupons like this:
   (1) Use up k coupons first, and then take out the k cheapest ones from c in succession.
   (2) Coupon replacement. Take p1 with the cheapest original price from p, c2 with the cheapest discount price from c, and then take d with the smallest discount from d: 1) If
   d > p1-c2, it means that it is not worthwhile to replace the coupon, and there is no need to replace it. . Buy the next piece of clothing at the original price for p1.
   2) If d ≤ p1-c2, it means that it is worthwhile to replace the coupon. The next piece of clothing can be bought at a discounted price c2, and the original price with a coupon is changed to the original price.
   This question is generally about greed, and three priority queues are used to deal with greed. But the coupon replacement operation is the "repentance" of greed, so it is called "repentance greedy". Greedy is to continuously perform local optimal operations, but sometimes when the local optimal cannot deduce the global optimal, you can use regret greed to cancel the previous decision and change the path to be greedy again. .
[Key point] Greedy.

C++ code

//代码参考 https://www.luogu.com.cn/blog/Leo2007-05-24/solution-p3045
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=50010;
int p[N],c[N];
bool buy[N];                         //buy[i]=1: 第i个物品被买了
int ans=0;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >P,C;
priority_queue<int,vector<int>,greater<int> >D;
signed main(){
    
    
	int n,k,m; cin>>n>>k>>m;
	for(int i=1;i<=n;i++){
    
    
		cin>>p[i]>>c[i];
		P.push(make_pair(p[i],i));    //原价,  还没买的在这里,如果买了就pop出去
		C.push(make_pair(c[i],i));    //优惠价,还没买的在这里,如果买了就pop出去
	}
	for(int i=1;i<=k;i++)   D.push(0);   //k张优惠券,开始时每个优惠为0
	while(!P.empty() && !C.empty()){
    
    
		pair<int,int> p1 = P.top();     //取出原价最便宜的
		pair<int,int> c2 = C.top();     //取出优惠价最便宜的
		if(buy[p1.second]){
    
     P.pop(); continue;}       //这个已经买了,跳过
		if(buy[c2.second]){
    
    	C.pop(); continue;}    //这个已经买了,跳过
		if(D.top() > p1.first-c2.first){
    
                  //用原价买i更划算,不用替换优惠券
			m -= p1.first;                           //买原价最便宜的
			P.pop();                    //这里不要C.pop(),因为买的是p1,不是c2
			buy[p1.second] = true;      //标记p1买了
		}
		else{
    
                                 //替换优惠券。头k个都先执行这里
			m -= c2.first+D.top();       //买优惠价最便宜的
			C.pop();                     //这里不要p.pop(),因为买的是c2,不是p1
			buy[c2.second]=true;         //标记c2买了
			D.pop();                     //原来用优惠券的退回优惠券
			D.push(p[c2.second]-c[c2.second]);   //c2使用优惠券,重新计算delta并进队列
		}
		if(m >= 0) ans++;
		else       break;
	}
	cout<<ans<<endl;
	return 0;
}

Java code

import java.util.*;
public class Main {
    
    
   public static void main(String[] args) {
    
    
       Scanner scanner = new Scanner(System.in);
       int n = scanner.nextInt();
       int k = scanner.nextInt();
       long m = scanner.nextLong();
       int[] p = new int[n + 1];
       int[] c = new int[n + 1];
       boolean[] buy = new boolean[n + 1];
       PriorityQueue<int[]> P = new PriorityQueue<>((a, b) -> a[0] - b[0]);
       PriorityQueue<int[]> C = new PriorityQueue<>((a, b) -> a[0] - b[0]);
       PriorityQueue<Integer> D = new PriorityQueue<>();
       for (int i = 1; i <= n; i++) {
    
    
           p[i] = scanner.nextInt();
           c[i] = scanner.nextInt();
           P.add(new int[]{
    
    p[i], i});
           C.add(new int[]{
    
    c[i], i});
       }
       for (int i = 1; i <= k; i++)   D.add(0);
       long ans = 0;
       while (!P.isEmpty() && !C.isEmpty()) {
    
    
           int[] p1 = P.peek();
           int[] c2 = C.peek();
           if (buy[p1[1]]) {
    
     P.poll();  continue; }
           if (buy[c2[1]]) {
    
     C.poll();  continue; }
           if (D.peek() > p1[0] - c2[0]) {
    
    
               m -= p1[0];
               P.poll();
               buy[p1[1]] = true;
           } else {
    
    
               m -= c2[0] + D.peek();
               C.poll();
               buy[c2[1]] = true;
               D.poll();
               D.add(p[c2[1]] - c[c2[1]]);
           }
           if (m >= 0)    ans++;
           else           break;
       }
       System.out.println(ans);
   }
}

Python code

import heapq
n, k, m = map(int, input().split())
p = [0] * (n+1)
c = [0] * (n+1)
buy = [False] * (n+1)
P = []
C = []
D = []
for i in range(1, n+1):
    p[i], c[i] = map(int, input().split())
    heapq.heappush(P, (p[i], i))
    heapq.heappush(C, (c[i], i))
for i in range(1, k+1):  heapq.heappush(D, 0)
ans = 0
while P and C:
    p1 = P[0]
    c2 = C[0]
    if buy[p1[1]]: heapq.heappop(P); continue
    if buy[c2[1]]: heapq.heappop(C); continue
    if D[0] > p1[0]-c2[0]:
        m -= p1[0]
        heapq.heappop(P)
        buy[p1[1]] = True
    else:
        m -= c2[0]+D[0]
        heapq.heappop(C)
        buy[c2[1]] = True
        heapq.heappop(D)
        heapq.heappush(D, p[c2[1]]-c[c2[1]])
    if m >= 0:    ans += 1
    else:       break
print(ans)

Guess you like

Origin blog.csdn.net/weixin_43914593/article/details/132535771
Recommended