2023第14届蓝桥杯大赛软件赛省赛C/C++大学A组第6题题解:买瓜

文章介绍了在一个瓜摊问题中,如何通过深度优先搜索(DFS)的暴力模拟和剪枝策略,最小化切割瓜的数量,以求得总重量恰好为m的瓜。两种方法对比,剪枝方法显著提高了效率,适用于大数据范围的输入

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

问题描述:

方法一:dfs暴力模拟(45%)

方法二:dfs剪枝(100%)


问题描述:

        小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜,每个瓜的重量为 Ai 。 小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈 一刀。 小蓝希望买到的瓜的重量的和恰好为m。 请问小蓝至少要劈多少个瓜才能买到重量恰好为 m 的瓜。如果无论怎样小 蓝都无法得到总重恰好为 m 的瓜,请输出 1

输入格式:

        输入的第一行包含两个整数 n, m,用一个空格分隔,分别表示瓜的个数和 小蓝想买到的瓜的总重量。 第二行包含 n 个整数 Ai,相邻整数之间使用一个空格分隔,分别表示每个 瓜的重量。

输出格式:

        输出一行包含一个整数表示答案。

评测用例规模与约定:
        对于20%的评测用例,1≤n ≤10 ;
        对于60%的评测用例,1≤n ≤20 ;
        对于100%的评测用例,1≤n≤30,1≤Ai≤10^9,1 ≤m≤ 10^9。

方法一:dfs暴力模拟(45%)

        将所有瓜按顺序排成一排,从前往后选择每个瓜。对于每一个瓜,有三种选择:不要,切一半,拿整个。分别切了0,1,0刀。不能切一刀之后两半全拿,这样不符合题意的最少劈的次数。
        可以使用dfs模拟这个从前往后选择的过程,dfs(id,sum,cut)中的三个参数分别表示当前选择的是第几个瓜,之前一共拿了多少重量的瓜,之前一共劈了多少刀。上面说的三种选择对应的函数就是:
        1、不拿dfs(id+1,sum,cut)
        2、切一刀,拿一半dfs(id+1,sum+a[id]/2,cut+1)
        3、不切,拿整个dfs(id+1,sum+a[id],cut)
        当sum==想要的重量的m时,就可以比较当前劈的次数与之前最少的次数哪个更小了。
#include <iostream>
using namespace std;
double a[35];
int n,m;
int ans=35;
void f(int id,double sum,int cut)//当前选择第几个瓜,已拿重量,已劈次数
{
  if(sum==m)//拿够了
  {
    ans=min(ans,cut);
    return ;
  }
  if(id>n) return ;//所有瓜都选择完了
  f(id+1,sum,cut);//不拿这个瓜
  f(id+1,sum+a[id]/2,cut+1);//切一刀,拿一半
  f(id+1,sum+a[id],cut);//不切,拿整个
}
int main()
{
  cin>>n>>m;
  for(int i=1;i<=n;i++)
    cin>>a[i];
  f(1,0,0);
  if(ans<35) cout<<ans;
  else cout<<"-1";
  return 0;
}

方法二:dfs剪枝(100%)

        分析上一个代码,当n=30时执行了3^30次dfs函数,其中有很多情况是不需要执行的。剪枝有以下几种思路:

        1、当前获得的总重量已经超过目标m,不需要继续执行。

        2、当前劈的次数超过了目前最少的次数,不需要继续执行。

        3、如果之后所有的瓜全部都要,加上当前的重量,也不能达到目标m,不需要继续执行。为了快速计算之后所有瓜的重量,可以使用前缀和预处理瓜的重量。为了更早进行剪枝,可以将瓜的重量从大到小排序。

#include <iostream>
#include<algorithm>
using namespace std;
double a[35];
double b[35];
int n,m;
int ans=35;
void f(int id,double sum,int cut)//当前选择第几个瓜,已拿重量,已劈次数
{
  if(sum==m)//拿够了
  {
    ans=min(ans,cut);
    return ;
  }
  if(id>n) return ;
  if(sum>m||cut>ans) return ;
  if(sum+b[n]-b[id-1]<m) return ;//剩下的瓜全拿,也不够目标重量
  f(id+1,sum,cut);//不拿这个瓜
  f(id+1,sum+a[id]/2,cut+1);//切一刀,拿一半
  f(id+1,sum+a[id],cut);//不切,全拿
}
int main()
{
  cin>>n>>m;
  for(int i=1;i<=n;i++)
    cin>>a[i];
  sort(a+1,a+n+1,greater<double>());//从大到小排序
  for(int i=1;i<=n;i++)
    b[i]=a[i]+b[i-1];//前缀和预处理
  f(1,0,0);
  if(ans<35) cout<<ans;
  else cout<<"-1";
  return 0;
}

        注意需要将瓜的重量除以2,直接用int型/2是整除应该使用double型,或者将所有瓜的重量和m都乘以2,由于数据范围时10^9,最好使用long long型。

        我在写这个题的时候将数组下标从1开始存,排序的时候sort第二个参数应该加n+1,因为这里写错了导致卡了很久,去读了官方这题的题解感觉很奇怪,将瓜分成两部分分别dfs,后一部分dfs时再二分搜索前一部分存的对应重量的刀数相加。

目前尚未有2024年第十四蓝桥杯大赛软件C/C++大学B的真题解发布,因为该事的时间线可能还未到达公布阶段[^1]。然而,可以基于以往的比形式和内容推测其考察的知识点范围以及提供一些常见的练习方向。 以下是关于如何准备此类比的一些指导: ### 准备指南 #### 一、熟悉基础算法 掌握基本的数据结构和经典算法对于参者至关重要。这包括但不限于数、链表、栈、队列等数据结构的应用;排序(快速排序、归并排序)、查找(二分法)、动态规划等问题解决方法的学习与实践。 ```cpp // 快速排序实现 (C++) void quickSort(int arr[], int low, int high){ if(low < high){ int pi = partition(arr,low,high); quickSort(arr, low, pi-1); quickSort(arr, pi+1, high); } } int partition (int arr[], int low, int high){ int pivot = arr[high]; int i = (low - 1); for (int j = low; j <= high- 1; j++){ if (arr[j] < pivot){ i++; swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); return (i + 1); } ``` ```java // 快速排序实现 (Java) public static void quickSort(int[] array, int start, int end) { if(start >= end) return; int pivotIndex = partition(array, start, end); quickSort(array, start, pivotIndex - 1); quickSort(array, pivotIndex + 1, end); } private static int partition(int[] array, int start, int end) { int pivotValue = array[end]; int index = start; for(int i=start;i<end;i++) { if(array[i]<pivotValue) { swap(array,i,index++); } } swap(array,end,index); return index; } ``` #### 二、深入理解编程语言特性 无论是使用C++还是Java参加竞,都需要深入了解所选语言的特点及其标准库的功能。例如,在C++中熟练运用STL容器类如vector、map等能够极大提高编码效率;而在Java里,则需熟知Collections框架下的各类集合类型及其实现原理。 #### 三、模拟实战训练 通过历年试进行反复演练是非常有效的备考方式之一。虽然现在无法获取到最新的2024年具体目,但是可以通过分析往年的考来预测可能出现的新颖考点,并针对性加强薄弱环节。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值