记录一道面试算法题餐馆问题(贪心和动态规划)

在这里插入图片描述
太难受了,面试时看成桌子都是一样大的了。一拍脑袋就用动态规划做了…害估计凉了。
我当时的思路是这样的(并不是此题正确思路,没兴趣可跳过):
假如桌子的大小时一致的,我们可以转换为一个普通的01背包问题,客人就是选与不选的问题,不过需要将客人的人数除以桌子容量向上取整转换为需要桌子的张数。
如果想要知道n批顾客,i张桌子最大收益为多少,那么只要知道i-1张再加上目前可供选择的没入座的客人批次即可。
定义:
dp[i][j] :第i批客人来时j个桌子最大收益为多少
转移方程:
x为此次客人需要的桌子数,rmb为此次客人的消费金额
dp[i][j] = 如果当前桌子的张数大于所需桌子张数 max(dp[i-1][j],dp[i-1][j-x]+rmb) ; 如果dp[i][j] = 如果小于 dp[i-1][j];
初始化:

代码懒得再写完了,只把核心部分写一下…空间还能压缩一下,压缩成dp[n]:n张桌子的最大收益,因为获取的都是上一列前面行的数字所以从后往前更新即可.

int[] dp = new int[n+1];
for(int i=0;i<m;i++){
	for(int j=n;i>=1;j--){
		if(x<=a){
			dp[j] = max(dp[j],dp[j-x]+rmb);	
		}
	}
}

正确的思路:

动态规划
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
杀了我把…太淦了
如果还是用动态规划做。
状态的定义还是一样的。唯一不同的是需要模拟一种前包含后的情况,就是说前面可以后面一定可以,怎么实现呢?排序,将客人的人数以及餐桌的大小均由小到大排序。
定义:
dp[i][j] :第i批客人来时j个桌子最大收益为多少
然后转移方程变为:
i批客人人数x,j张桌子能容纳人数k
dp[i][j]:如果 x<=k,max(dp[i-1][j],dp[i-1][j-1]+rmb); else dp[i-1][j]
初始化:

压缩空间:
缩成dp[n]:n张桌子的最大收益,因为获取的都是上一列前面行的数字所以从后往前更新即可.

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
 
/**
 * @创建人 YDL
 * @创建时间 2020/5/9 22:34
 * @描述
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
         int n = sc.nextInt();
         int m = sc.nextInt();
         int[] a = new int[n+1];
         for(int i=0;i<n;i++){
             a[i+1] = sc.nextInt();
         }
         Arrays.sort(a);
 
         int[][] nums = new int[m][2];
         for(int i=0;i<m;i++){
            nums[i][0] = sc.nextInt();
            nums[i][1] = sc.nextInt();
         }
         Arrays.sort(nums,(Comparator.comparingInt(o -> o[0])
         ));
         long[] dp = new long[n+1];
        for(int i=0;i<m;i++){
            for(int j=n;j>=1;j--){
                 if(nums[i][0]<=a[j]) {
                    dp[j] = Math.max(dp[j-1]+nums[i][1],dp[j]);
                 }else{
                     break;
                 }
             }
         }
         System.out.println(dp[n]);
    }
}

不过超时了,过了80%的数据,说明思路是没有错的。
这超时了也就是说O(n2)的时间复杂度不行。那四舍五入就是说动态规划不能用呗(或者有其他的定义状态的方式)
贪心
那么还有其他思路嘛?其实这题用贪心的思路更好做啊,反正都要排序,那干脆排得彻底一点嘛。
我们想要最大的收益,那么消费比较高的客人就优先排座,也就是说消费高的优先(消费降序),
如果消费一样的呢?那就人均消费高的就比较受店家欢迎了,相同的收益再按照人数升序排列,这样比较靠前的就是对商家来说优质的客户了,优先给它们安排位置。

咋一看这还是得O(n2),不太中啊,别急。

我们的桌子也按从小到达排序,优先给客人安排尽量小却又可以容纳客人的桌子。既然桌子是顺序的,那么我们就可能采用二分查找,找到恰好可以容纳当前客人的最小桌子。(真是机(奸)智(诈)的老板呀)

import java.util.*;
 
/**
 * @创建人 YDL
 * @创建时间 2020/5/9 22:34
 * @描述
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
         int n = sc.nextInt();
         int m = sc.nextInt();
         List<Integer> a = new ArrayList<>();
         for(int i=0;i<n;i++){
             a.add(sc.nextInt());
         }
         a.sort((Comparator.comparingInt(o -> o)));
 
         int[][] nums = new int[m][2];
         for(int i=0;i<m;i++){
            nums[i][0] = sc.nextInt();
            nums[i][1] = sc.nextInt();
         }
         Arrays.sort(nums,(o1,o2)->{
             if(o1[1]==o2[1]){
                 return o1[0]-o2[0];
 
             }else{
                 return o2[1]-o1[1];
             }
         });
        long max = 0;
         for(int i=0;i<m&&!a.isEmpty();i++){
            int left=0,right=a.size()-1,rmb = nums[i][1],size = nums[i][0];
            while(left<right){
                int mid = (left+right)>>1;
                if(a.get(mid)==size){
                    left =mid;
                    break;
                }else if(a.get(mid)<size){
                    left = mid+1;
                }else{
                    right = mid;
                }
            }
            if(size<=a.get(left)){
                max+=rmb;
                a.remove(left);
            }
        }
         System.out.println(max);
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值