蓝桥杯:卡牌

蓝桥杯:卡牌icon-default.png?t=N2N8https://www.lanqiao.cn/problems/2191/learning/

目录

问题描述

输入格式

输出格式

样例输入

 样例输出

样例说明

数据范围

题目分析(贪心+二分)

AC代码(Java):


问题描述

        这天, 小明在整理他的卡牌。

        他一共有 n 种卡牌, 第 i 种卡牌上印有正整数数 i(i∈[1,n]), 且第 i 种卡牌 现有 ai​ 张。

        而如果有 n 张卡牌, 其中每种卡牌各一张, 那么这 n 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌, 拿出了 m 张空白牌, 他可以在上面写上数 i, 将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观, 决定第 i 种牌最多手写 bi​ 张。

        请问小明最多能凑出多少套牌?

输入格式

        输入共 3 行, 第一行为两个正整数 n,m 。

        第二行为 n 个正整数 a1​,a2​,…,an​ 。

        第三行为 n 个正整数 b1​,b2​,…,bn​ 。

输出格式

        一行, 一个整数表示答案。

样例输入

4 5
1 2 3 4
5 5 5 5

 样例输出

3

样例说明

        这 5 张空白牌中, 拿 2 张写 1 , 拿 1 张写 2 , 这样每种牌的牌数就变为了 3,3,3,43,3,3,4, 可以凑出 3 套牌, 剩下 2 张空白牌不能再帮助小明凑出一套。

数据范围

        对于 30% 的数据, 保证 n≤2000;

        对于 100% 的数据, 保证 n≤2×10^5; ai​,bi​≤2n; m≤n^2 。

题目分析(贪心+二分)

        这道题目如果按照暴力来思考的话,就是先找出给出的牌中能够拼凑出多少套,然后依次给最低的几个牌补1,直到m没有。

        根据数据范围来看,如果是N^2,肯定会超时。所以选择比N^2时间复杂度小的算法来处理,那么就应该时间复杂度为N(log2N)的二分了。

        二分的前提,是满足二段性,那么我们假设可以拼凑出X套牌。那么二段性就分为我们是否可以拼凑出X张牌。

        之后写一个check算法,检查a[i]是否够X,如果不够,那么判断b[i](可以用空白牌添加)加上a[i]是否能够X,如果够了,那么我们就从空白牌m之中减去这部分,相当于将空白牌添加上去了。如果空白牌的数量不够,那么肯定是不能拼凑成X张牌的。然后继续判断下一张牌,最终遍历完成,能够将所有卡牌拼凑出X,那么肯定能够拼凑出X张牌。

        因此我们二分N (牌的种类) 即可。

        需要注意的是,m的范围是n^2,那么肯定会超出整型,必须用long来接受。

        还有就是这道题是C++组的国赛题目,对于C++来说,运行时间1s处理这个数据是肯定没有问题的。但是Java如果用Scanner,就会超时,所以要换成StreamTokenizer。

Scanner (只过30%,其余超时):

 

StreamTokenizer(AC):

 

AC代码(Java):

import java.util.*;
import java.io.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    static int N;
    static long M;
    static int[] card;
    static int[] upperLimit;

    static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

    public static int nextInt() throws IOException{
      st.nextToken();
      return (int) st.nval;
    }

    public static long nextLong() throws IOException{
      st.nextToken();
      return (long) st.nval;
    }

    public static void init() throws IOException{
      N = nextInt(); //一共有n种牌
      M = nextLong(); //空白牌
      card = new int[N+1];
      upperLimit = new int[N+1];
      for(int i = 1;i<=N;i++) {
        card[i] = nextInt();
      }
      for(int i = 1;i<=N;i++) {
        upperLimit[i] = nextInt();
      }

    }

    public static void main(String[] args) throws IOException{
        init();
        //二分枚举,不然操作太麻烦了
        //我们设定一个x,二段性为x是否可以凑出x套牌。
        int left = 1;
        int right = N;
        long ans = 0;
        while(left < right) {
          int middle = (left+right) >> 1;
          if(check(middle)) { //可用凑出x套牌,
            ans = Math.max(ans,middle);
            left = middle+1;
          }else {
            right = middle;
          }
        }

        System.out.println(ans);
    }

    //判断是否可以拼凑成x套牌
    public static boolean check(int x) {
      //记录下可用的空白牌
      long m = M;
      //遍历card,如果card[i] != x,那么先 判断可用牌upperLimit[i]是否能够凑到x
      //如果可用凑到x,那么就减少m,如果m不足,也是不能拼接成功
      for(int i = 1;i<=N;i++) {
        //i卡牌肯定能凑出x套,所以跳过
        if(card[i] >= x) continue;
        //如果i卡牌加上可用卡牌还是小于x,肯定不能凑出x套
        if(card[i]+upperLimit[i] < x) return false;
        //用空白牌补充i卡牌,令其满足x张卡牌的最低标准
        m -= Math.abs(x-card[i]);
        //如果空白牌不够用了也是不能凑出x套
        if(m < 0) return false;
      }

      return true;
    }
}

       

         

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值