算法笔记之蓝桥杯&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;
}
}
}
}