十四届蓝桥杯Java B组题解(蜗牛和买二赠一)

题目描述(蜗牛)

这天,一只蜗牛来到了二维坐标系的原点。 在 x 轴上长有 n 根竹竿。它们平行于 y 轴,底部纵坐标为 0 ,横坐标分别 为 x 1 , x 2 , ..., x n 。竹竿的高度均为无限高,宽度可忽略。蜗牛想要从原点走到第 n 个竹竿的底部也就是坐标 ( x n , 0) 。它只能在 x 轴上或者竹竿上爬行,在 x 轴上爬行速度为 1 单位每秒;由于受到引力影响,蜗牛在竹竿上向上和向下爬行 的速度分别为 0 . 7 单位每秒和 1 . 3 单位每秒。 为了快速到达目的地,它施展了魔法,在第 i 和 i + 1 根竹竿之间建立了传送门(0 < i < n ),如果蜗牛位于第 i 根竹竿的高度为 a i 的位置 ( x i , a i ) ,就可以 瞬间到达第 i + 1 根竹竿的高度为 b i +1 的位置 ( x i +1 , b i +1 ), 请计算蜗牛最少需要多少秒才能到达目的地。

输入格式: 

输入共 1 + n 行,第一行为一个正整数 n ;
第二行为 n 个正整数 x 1 , x 2 , . . . , x n ;
后面 n − 1 行,每行两个正整数 a i , b i +1 。

 输出格式:

输出共一行,一个浮点数表示答案( 四舍五入保留两位小数 )。

样例输入:

3
1 10 11
1 1
2 1

样例输出:

4.20

样例说明: 

蜗牛路线:(0 , 0) → (1 , 0) → (1 , 1) → (10 , 1) → (10 , 0) → (11 , 0) ,花费时间为 1 +1/  0.7 + 0 + 1/1 .3 + 1 ≈ 4 . 20

测评用例规模与约定:

对于 20 % 的数据,保证 ≤ 15 ;

对于 100 % 的数据,保证 ≤ 10⁵ ,a b ≤ 10⁴ ,x ≤ 10⁹ 。

题目解读

看到题目第一想法是使用动态规划求解,那么根据题意,我们会有两个状态:一个是走传送门,一个是不走传送门。创建一个dp数组,dp[i][0]表示从起点走到第i个杆子底部的时间,dp[i][1]表示从起点走到第i个杆子的传送门的时间。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n=scan.nextInt();  //n根竹竿
        int[] arr=new int[n+1]; //x轴上每个杆子的坐标
        int[] a=new int[n+1];  //传送门起始坐标
        int[] b=new int[n+1];  //传送门终点坐标
        double[][] dp=new double[n+1][2];  //因为只有两个状态转移数量,所以开两位

        for(int i=1;i<=n;i++){
          arr[i]=scan.nextInt();
        }
        for(int i=1;i<n;i++){
          a[i]=scan.nextInt();
          b[i]=scan.nextInt();
        }
        
        dp[1][0]=arr[1]/1.0;  //从起点到第一根杆子底部的时间
        dp[1][1]=arr[1]+a[1]/0.7;  //从起点到第一个传送门起点的时间

        for(int i=2;i<=n;i++){
          //从上一根杆子的底部爬到下一根底部用时和从上一个传送门终点到下一个传送门起点用时比较
          dp[i][0]=Math.min(dp[i-1][0]+(arr[i]-arr[i-1]),dp[i-1][1]+b[i-1]/1.3);

          //这里分两种情况:1.上一个传送门终点在下一个传送门起点上面或重合
          //2.上一个传送门终点在下一个传送门起点下面
          if(b[i-1]>=a[i]){
          //上一个传送门终点到下一个传送门起点的时间和到下一个传送门终点所在杆子底部的时间比较
            dp[i][1]=Math.min(dp[i-1][1]+(b[i-1]-a[i])/1.3,dp[i][0]+a[i]/0.7);
          } else{
            dp[i][1]=Math.min(dp[i-1][1]+(a[i]-b[i-1])/0.7,dp[i][0]+a[i]/0.7);
          }
        }
        System.out.printf("%.2f",dp[n][0]);  //答案保留两位小数
    }
}

题目描述(买二赠一)

某商场有 件商品,其中第 件的价格是 A i 。现在该商场正在进行 “ 买二 赠一” 的优惠活动,具体规则是: 每购买 2 件商品,假设其中较便宜的价格是 P (如果两件商品价格一样,

则 等于其中一件商品的价格),就可以从剩余商品中任选一件价格不超过 P / 2 的商品,免费获得这一件商品。可以通过反复购买 2 件商品来获得多件免费商 品,但是每件商品只能被购买或免费获得一次。 小明想知道如果要拿下所有商品(包含购买和免费获得),至少要花费多少钱?

输入格式:

第一行包含一个整数 N 。

第二行包含 个整数,代表 A 1 , A 2 , A 3 , . . . , A N

输出格式:

输出一个整数,代表答案。

样例输入:

7
1 4 2 8 5 7 1

样例输出:

25

样例说明:

小明可以先购买价格 4 和 8 的商品,免费获得一件价格为 1 的商品;再后

买价格为 5 和 7 的商品,免费获得价格为 2 的商品;最后单独购买剩下的一件

价格为 1 的商品。总计花费 4 + 8 + 5 + 7 + 1 = 25 。不存在花费更低的方案。

评测用例规模与约定:

对于 30 % 的数据, 1 ≤ ≤ 20 。

对于 100 % 的数据, 1 ≤ ≤ 5 × 10⁵ ,1 ≤ A ≤ 10⁹ 。

题目解读

求最少的花费我们可以先购买两件较贵的商品,这样我们拿到的赠品价值也较高,最后累计花费钱数也会更少。我的想法是先对商品价值进行由小到大排序,然后从后开始选取两件商品,再使用二分法来寻找价值最高且能够赠送的商品,最后找到答案。由于一个商品只购买或赠送一次,我们需要创建一个boolean型数组对已选商品进行标记。

代码

import java.util.Scanner;
import java.util.Arrays;

public class Main {
    static int N=500010;
    static int[] a=new int[N];
    static boolean[] b=new boolean[N];
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n=scan.nextInt(); 
        for(int i=0;i<n;i++) {
            a[i]=scan.nextInt();
        }

        Arrays.sort(a,0,n);  //排序下标从0到n的数
        int count=0;  //记录拿商品的个数
        long sum=0;  //记录总花费
        int index=n-1;  

        while(index>=0) {
            if(b[index]) {  //判断改商品是否被购买或赠送
                index--;
                continue;
            }
            sum+=a[index];
            b[index]=true;
            count++;
            if(count%2==0) {  //二分
                int m=a[index]/2;
                int l=0,r=index;
                while(l<r){
                  int mid=l+r+1 >> 1;
                  if(a[mid]>m || b[mid]){
                    r=mid-1;
                  } else{
                    l=mid;
                  }
                }
                if(l==0){
                    if(a[l]>m){
                      continue;
                    }
                  }
                b[l]=true;
            }
        }
        System.out.println(sum);
        scan.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值