[算法]订单问题

订单问题

问题地址

问题描述

Description
Rahul and Ankit are the only two waiters in Royal Restaurant. Today, the restaurant received N orders. The amount of tips may differ when handled by different waiters, if Rahul takes the ith order, he would be tipped Ai rupees and if Ankit takes this order, the tip would be Bi rupees.In order to maximize the total tip value they decided to distribute the order among themselves. One order will be handled by one person only. Also, due to time constraints Rahul cannot take more than X orders and Ankit cannot take more than Y orders. It is guaranteed that X + Y is greater than or equal to N, which means that all the orders can be handled by either Rahul or Ankit. Find out the maximum possible amount of total tip money after processing all the orders.
Input
• The first line contains one integer, number of test cases.
• The second line contains three integers N, X, Y.
• The third line contains N integers. The ith integer represents Ai.
• The fourth line contains N integers. The ith integer represents Bi.
Output
Print a single integer representing the maximum tip money they would receive.
Sample Input 1

1
5 3 3
1 2 3 4 5
5 4 3 2 1

Sample Output 1

21
题目解析
  1. A不能接受超过X个订单,B不能接受超过Y个订单。它保证X+Y大于或等于N
  2. 每个订单只能由一个人(A或B)处理
  3. 同样的订单由A或B处理有不同的报酬,
  4. 通过正确的分配订单令总报酬最大
思路解析
思路1

类似贪心的思想,每次选择收益最大的订单交给可以获得最大收益的人去做

我们可以这样看待问题,若订单可以两个人一起处理,那么我们所获得的总共的消费就是sum(arr_A)+sum(arr_B).但是我们必须只能由一个人来处理,因此必须要作出取舍.

对于一个新来的订单,若让A做,获得的收益是a[i]-b[i],若让B做,获得的收益是b[i]-a[i],若a[i]-b[i]>0,让A做就会有收益,让B做就会有亏损

为了最大化我们的收益,我们先选择abs(a[i]-b[i])最大的先做,直到A或B一方不能处理之前,我们的收益都是最大的.并且在A或B一方不能处理之后,我们的亏损是最小的(因为收益最大的已经都做完了)

这个做法十分简便易行,复杂度只有O(N)

思路2

使用动态规划
最优问题(最大化小费)可以在接完前n-1单的基础上再选第n个,即max(n) =max(n-1) +(A or B),可以从后往前倒着理解

如何创建dp数组?
我们可以假设订单是一个一个来的

  • i,j分别代表A和B接单的数量
  • i+j代表当前A和B总共接到的单数
  • dp [i] [j] A接了i单,B接了j单的情况下的最优解

如何更新dp数组?

if i + j <= n:
   # 这一行代表了如果A接单,那么当前的值就是上一次A接了i-1单,b接了j单,得到了A接i单,b接j单
   a_add = dp[i - 1][j] + arr_A[i + j - 1]  # a接单
	
   # 这一行代表了如果B接单,那么当前的值就是上一次B接了i单,b接了j-1单,得到了A接i单,b接j单 
   b_add = dp[i][j - 1] + arr_B[i + j - 1]  # b接单
   dp[i][j] = max(a_add, b_add)  #将两次的值做比较,并更新
else:
    #当i + j > n:时,代表此时已经不能接单,但我们需要将几种接单情况作比较,并把最大值传递
   dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

eg:

用例:
5 3 4
8 7 5 9 6 
1 7 5 1 2 

矩阵初始化:				在满足x+y<=N时的矩阵情况		  矩阵更新完毕
[[ 0  1  8 13 14]		[[ 0  1  8 13 14]		   [[ 0  1  8 13 14]
 [ 8  0  0  0  0]	 	 [ 8 15 20 22 24]	 		[ 8 15 20 22 24]
 [15  0  0  0  0]	 	 [15 20 29 31  0]	 		[15 20 29 31 31]
 [20  0  0  0  0]]	 	 [20 29 35  0  0]]	 		[20 29 35 35 35]]

请看最后一个矩阵

在这里插入图片描述

斜线上方着X+Y<=N时的情况,方块代表X+Y=N的情况,下方代表X+Y>N的情况

对于X+Y>N的情况实际上就是把方块里的某个最大值传递到最右下角

代码实现
思路1:

python

if __name__ == '__main__':
    for _ in range(int(input())):
        n, a_num, b_num = map(int, input().split())
        arr_A = list(map(int, input().split()))
        arr_B = list(map(int, input().split()))
        diff_arr = [x - y for x, y in zip(arr_A, arr_B)]  # 求A和B的差值,
        index_arr = list(range(n))  # 创建对应索引数组
        diff_index_zip = zip(diff_arr, index_arr)  # 打包成元组,即键值对{key=diff,val=index},键是差值,值是原数组下标
        diff_index_zip = sorted(diff_index_zip, key=lambda x: -abs(x[0]))  # 排序,按照键值的绝对值从大到小排序
        max_sum = 0
        for k, v in diff_index_zip:  # k=diff,v=index
            if k > 0:  # 此时是A大,优先A处理
                if a_num > 0:  # 若A还能处理
                    a_num -= 1 # A--
                    max_sum += arr_A[v] #加上用A处理的收益
                else:  # 只能交给B了
                    b_num -= 1
                    max_sum += arr_B[v]
            else:  # 此时是B大
                if b_num > 0:  # 若B还能处理
                    b_num -= 1
                    max_sum += arr_B[v]
                else: # 只能交给A了
                    a_num -= 1
                    max_sum += arr_A[v]
        print(max_sum)
思路2:

python

if __name__ == '__main__':
    for _ in range(int(input())):
        n, a_num, b_num = map(int, input().split())
        arr_A = list(map(int, input().split()))
        arr_B = list(map(int, input().split()))
        dp = [[0] * (b_num + 1) for _ in range(a_num + 1)]  # a行b列,列来储存A,行来储存B
        for i in range(1, b_num + 1):  # 初始化第一行行,也就是B
            dp[0][i] = dp[0][i - 1] + arr_B[i - 1]
        for i in range(1, a_num + 1):  # 初始化第一列,也就是A
            dp[i][0] = dp[i - 1][0] + arr_A[i - 1]
        # 更新dp数组, i+j表示共有第几个订单
        # i表示A接单,j表示B接单
        # 因为数组是从0开始的,i + j - 1代表着数组下标
        for i in range(1, a_num + 1):
            for j in range(1, b_num + 1):
                if i + j <= n:
                    a_add = dp[i - 1][j] + arr_A[i + j - 1]  # a接单
                    b_add = dp[i][j - 1] + arr_B[i + j - 1]  # b接单
                    dp[i][j] = max(a_add, b_add)
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
        print(dp[-1][-1])

java:

import java.io.*;
import java.util.*;

class Main
 {
	public static void main (String args[])
	 {
    Scanner sc = new Scanner(System.in);
	    int tc= sc.nextInt();
	    while(tc-- >0)
	    {
	        int N= sc.nextInt();
	        int X= sc.nextInt();
	        int Y= sc.nextInt();

	        int A[]=new int[N];
	        int B[]=new int[N];

	        for(int i=0;i<N;i++)
	            A[i]=sc.nextInt();
	        for(int i=0;i<N;i++)
	            B[i]=sc.nextInt();

	       int dp[][]=new int[X+1][Y+1];

	        for(int i=1;i<=X;i++)
	            dp[i][0]= A[i-1] + dp[i-1][0];
	        for(int i=1;i<=Y;i++)
	            dp[0][i]= B[i-1] + dp[0][i-1];

	       for(int i=1;i<=X;i++)
	       {
	           for(int j=1;j<=Y;j++)
	           {
	               if(i+j <= N)
	               {
	                   int op1= A[i+j-1] + dp[i-1][j]; // GIVING TO X (CHECK X IN FOR)-> ROW
	                   int op2= B[i+j-1] + dp[i][j-1]; // TO Y

	                   dp[i][j] = Math.max( op1 , op2 );
	               }
	               else
	                dp[i][j] = Math.max( dp[i-1][j] , dp[i][j-1] );
	           }
	       }

	       System.out.println(dp[X][Y]);
	    }
	 }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值