【手把手刷CCF】202209-2-何以包邮100(读过必懂,含思路解释、详细注释与代码)

故事的开头总是极尽温柔,故事会一直温柔……💜

✨你好啊,我是“ 怪& ”,是一名在校大学生哦。
🌍主页链接:怪&的个人博客主页
☀️博文主更方向为:课程学习知识、作业题解、期末备考。随着专业的深入会越来越广哦…一起期待。
❤️一个“不想让我曾没有做好的也成为你的遗憾”的博主。
💪很高兴与你相遇,一起加油!

一、🌳代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
int n;

const int N =40;
long long a[N];
int b[N];//记录第i本书是否被放入 
long long m;
long long out=99999999999;

void bt(int i,int sum1){ 
	long long x=sum1+a[i]; //将a[i]放入 
	if(x>=m){//超出m,后续不需再算入  
		if(x<out) out=x;//更新out 
		
		int t_flag=0;
		for(int i=1;i<=n;i++){//找到已放入的最大数的坐标t_flag 
			if(b[i]==1) t_flag=i; 
		} 
		if(t_flag!=0){
			sum1=sum1-a[t_flag];
			b[t_flag]=0;
			bt(t_flag+1,sum1);//不放a[t_flag],继续进行  	
		}
	}
	else{//未超出m 
		sum1=x;//将a[i]算入
		//		cout<<"sum1  "<<sum1<<endl;
		b[i]=1;
		
		int sum_sy=0;
		for(int j=i+1;j<=n;j++) sum_sy+=a[j]; 
		if(i!=n && sum_sy+sum1>=m) bt(i+1,sum1); //i未至n,并且剩余和+sum1可满足题意再继续 
		else{//i走到n还不够 
			sum1=sum1-a[i];//不放a[i] 
			b[i]=0;
			int t_flag=0;
			for(int i=1;i<=n;i++){//找到已放入的最大数的坐标t_flag 
				if(b[i]==1) t_flag=i; 
			} 
			if(t_flag!=0){
				sum1=sum1-a[t_flag];
				b[t_flag]=0;
				bt(t_flag+1,sum1);//不放a[t_flag],继续进行  	
			}
		} 
	}
} 

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	sort(a+1,a+n+1);//排序 	
	
	bt(1,0); 
	cout<<out<<endl; 
	return 0;
}

二、🌵解题思路

1、性质归纳🍠

(1)、题目提示

请添加图片描述

2、输入输出🍅

请添加图片描述

(1)、输入:
  • 图书数量:n
  • 包邮条件(需要满足的最低阈值):x
  • 第i本书的价格:a[1]、a[2]、a[3]……a[n]
(2)、求解:
  • 求解满足包邮条件下的最少花费:即求一组i使得Min(∑ai) 且 (∑ai)>x

3、算法思路与求解步骤✌

a、分析:
  • 目标:即求一组i使得Min(∑ai) 且 (∑ai)>x
  • 抽象:即选部分物品,求和其价格和最小且满足>x
  • 联想:许多物品,单个物品放与不放,联想至0/1背包问题

先前我写过的“0/1背包博文”:【动态规划法】0-1背包问题博文,点我即可直达哦~

b、结合此题的具体分析与步骤:
c、代码过程:
c1、准备/算法依托媒介:
  • 数组b[N]:其取值为0/1,表示第i本书有没有放入(第i本书的价格是否加入sum中)
  • out: 即所求的“满足包邮条件下的最小花费”(因一直与其比较,如何取最小值,所以其初值需设置无穷大/尽量大)
c2、过程:
  • 步骤一(代码中的sort(a+1,a+n+1);//排序 ):因为求解最小化问题,所以先排序方便剪枝,在下述过程中会讲如何剪枝)
  • 步骤二前半段(代码bt中的第一个大if:if(x>=m){//超出m,后续不需再算入 ):将a[i],放入。若已放入的书本价格求和结果x>=m,则后续的书本皆不需再进行判断.(假设此时放至第i本书),因为我们在步骤一进行排序,a[i+1]>=a[i]剪枝条件1
  • 步骤二后半段:注意此时的b[i]==0 (并没有放入,但是算入求和结果了,我复盘代码时愣了半天),先for循环找到放入第i本书之前已放入的书本序号:t_flag,然后使b[t_flag]=0(不放其),然后bt(t_flag+1,sum1);//不放a[t_flag],继续进行 (注意此时,sum1是没有算入第i和第t_flag价格,详见代码)
  • 步骤三前半段(放入i没有超出: else{//未超出m ): for(int j=i+1;j<=n;j++) sum_sy+=a[j]; if(i!=n && sum_sy+sum1>=m) bt(i+1,sum1); //i未至n,并且剩余和+sum1可满足题意再继续 剪枝条件2,即当前放入的书本后的(未遍历到的)所有的书本全放入所计算的价格和=sum<m,则不需要继续执行,这就是最后的30分剪枝,否则提交结果为70分哦)
  • 步骤三后半段(放入i以及后续所有书本还不够: else{//i走到n还不够 ):则同步骤二后半段继续进行遍历

❤️❤️❤️忙碌的敲代码也不要忘了浪漫鸭!

🔥静心复盘后呈现的博文,希望可以有点作用~
💕不知道我是否有讲懂呢,愿意的话可以评论区留言哦
🍅之前是不怎么吃肥肉的,后来,遇到了这里的猪脚饭~,味道真的很不错!
🌟别了三五天的雨;今,天终于放晴啦,最近也很开心,好不错好不错!

请添加图片描述

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪&

感谢您的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值