算法笔记之蓝桥杯&pat系统备考(3)

该博客围绕蓝桥杯算法备考,介绍深搜(DFS)和广搜(BFS),以01背包等为例说明DFS及剪枝,BFS由队列实现。重点阐述动态规划,包括递归和递推写法,通过斐波那契数列、数塔问题等讲解,还介绍最大连续子序列和、最长不降子序列等问题及状态设计方法。

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

算法笔记之蓝桥杯&pat系统备考(2)
多训练、多思考、多总结٩(๑•̀ω•́๑)۶

八、深搜和广搜

8.1DFS

dfs是一种枚举完所有完整路径以遍历所有情况的搜索方法,可以理解为每次都是一条路走到黑的犟种。
以老朋友斐波那契额数列为例,F(0)和F(1)可以视为死胡同,对于F(n)可以再走F(n-1)和F(n-2),依次类推。
简单应用:01背包

#include<iostream>
using namespace std;
const int maxn = 210;
int maxv = 0, n, m;
int w[maxn], v[maxn];

void dfs(int index, int sumW, int sumC){
   
	if(index == n){
   //完成对n间物品的选择,来到了死胡同
		if(sumW <= m && sumC > maxv) maxv = sumC;//出现不超过背包容量,且价值更大的方案则更新
		return;
	}
	dfs(index + 1, sumW + w[index], sumC + v[index]);//放入第index物品试试
	dfs(index + 1, sumW, sumC);//选择不放入第index件物品 
}

int main(){
   
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; i++){
   
		scanf("%d%d", w + i, v + i);
	}
	dfs(0, 0, 0);
	printf("%d", maxv);
	return 0;
} 

每件物品有两种选择,则上述代码的时间复杂度为O(2n)。当前想法是对所有物品确定后才判断是否超重,其实很有可能在过程中就已经超重了,完全可以在对每个物品的判断中都先确定不会超重的基础上继续选择。

#include<iostream>
using namespace std;
const int maxn = 210;
int maxv = 0, n, m;
int w[maxn], v[maxn];

void dfs(int index, int sumW, int sumC){
   
	if(index == n) return;
	if(sumW + w[index] <= m){
   
		if(sumC + v[index] > maxv) maxv = sumC + v[index];//更新最大价值 
		dfs(index + 1, sumW + w[index], sumC + v[index]);//放入试试
	}
	dfs(index + 1, sumW, sumC);//选择不放入 
}

int main(){
   
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; i++){
   
		scanf("%d%d", w + i, v + i);
	}
	dfs(0, 0, 0);
	printf("%d", maxv);
	return 0;
} 

这种通过题目条件的限制来节省计算量的方法称为剪枝。
上述的01背包中是一类常见的DFS问题的解决方法,即给定一个序列,枚举这个序列的所有子序列(可以不连续)。
牛刀小试:
蓝桥杯算法提高VIP-01背包
蓝桥杯-分糖果
蓝桥杯-飞机降落
蓝桥杯-小朋友崇拜圈
蓝桥杯-正则问题
1103 Integer Factorization
蓝桥杯-买瓜

8.2BFS

BFS广度优先搜索,当碰到岔道口时,先依次访问该岔道口能直接到达的所有结点,然后再访问这些结点能直接到达的结点,以此类推。
在这里插入图片描述
BFS一般由队列实现,且总是按层次的 顺序进行遍历,其基本写法为:

void bfs(int s){
   
	queue<int> q;//定义队列q
	q.push(s);//把起点s入队
	while(!q.empty()){
   
		取出队首元素top;
		访问队首元素top;
		把队首元素出队;
		把top的下一层结点中没入队的结点全部入队,并设置为已入队; 
	}
} 

在这里插入图片描述
即若干个(> 0)相邻的1为一个块,所谓相邻包括上下左右四个位置

#include<iostream>
#include<queue>
using namespace std;
const int maxn = 100;
struct node{
   //结点(x, y) 
	int x,y;
}Node;

int n, m, a[maxn][maxn], flag[maxn][maxn] = {
   0};
int X[4] = {
   0, 0, -1, 1};//增量数组 
int Y[4] = {
   -1, 1, 0, 0};//左 右 上 下 
 
bool judge(int x, int y){
   //判断是否需要访问点(x, y) 
	if(x < 0 || x >= n || y < 0 || y >= m) return false;//越界,非法坐标
	if(!a[x][y] || flag[x][y]) return false;//当前位置为0或者已经访问过,则跳过该点即可
	return true; 
} 
 
void bfs(int x, int y){
   
	queue<node> q;//辅助队列
	Node.x = x;//当前节点为(x, y) 
	Node.y = y; 
	flag[x][y] = 1;
	q.push(Node);//当前结点入队
	while(!q.empty()){
   //队列非空时循环 
		node top = q.front();//取出队首元素
		q.pop();//队首元素出队
		for(int i = 0; i < 4; i++){
   //得到上下左右的点坐标 
			int newX = top.x + X[i];
			int newY = top.y + Y[i];
			if(judge(newX, newY)){
   //若需要访问则入队 
				Node.x = newX;
				Node.y = newY;
				q.push(Node);
				flag[newX][newY] = 1;
			} 
		} 
	}
} 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值