深度优先搜索DFS入门

在图上寻找路径

策略:路径遍历

  • 只要能发现没走过的点,就走到它;
  • 多个点可走就随便挑一个
  • 如果无路可走就回退,再看看有没有走过的点可走

深度优先搜索定义(DFS

从起点出发,走过的点都做标记发现没走过的点就随意挑一个往前走,走不了就回退

int main(){
	将所有的点标记为新点
	起点=1
	终点=8
	cout<<Dfs(起点)
}
bool Dfs(V){//从V出发能不能走到终点
	if(V为终点){
		return true}
	if(V为旧点){
		return false}//V为旧点说明,从V出发过,没成功,可以直接返回从这个V出发不行
	将V标记为旧点;
	对和V相邻的每个结点U{
		if(Dfs(U)==true){
			return true;
		}
	}//是循环
	return false;
}

不仅要判断能不能走,而且需要记录路径

Node path[MAX_LEN];//MAX_LEN取节点总数即可
int depth;
bood Dfs(V){
	if(V为终点){
		path[depth]=V;
		return ture;
	}
	if(V为旧点){
		return false;
	}
	将V标记为旧点;
	path[depth]=V;
	depth++;
	对和V相邻的每个节点U{
		if(Dfs(U)==true){
			return ture;
		}
	}
	depth--;
	return false;//相当于是回退一个节点
}
int main(){
	将所有点标记为新点
	depth=0;
	if(Dfs(起点)){
		for(int i=0;i<=depth;i++){
			cout<<path[i]<<endl;
		}
	}
}

遍历图上所有节点//理解DFS的最坏时间复杂度

Dfs(V){
	if(V是旧点) return;
	将V标记为旧点;
	对和V相邻的每个点U{
		Dfs(U);
	}
}
int main(){
	将所有点标记为新点;
	while(在图中能找到新店k) Dfs(k);
}

图的表示方法

邻接矩阵

用一个二维数组G存放图G[i] [j]表示节点i和节点j之间边的情况(如有无边,边方向,权值大小等)

因此 G[i] [j]可以是一个Struct结构体类型

遍历复杂度是O(N^2)

邻接表

还可以让每个节点对应一个一维数组(Vector),里面存放从V连出去的边,边的信息包括另一顶点,还可能包括边权值等

遍历复杂度O(n+e),n为节点数目,e为边数目

例题——城堡问题2815//1164

建模思想:能把平面数字转化为图

解题思路

  • 将方块看作是节点,相邻两个方块之间如果没有墙,则在方块之间连一条边,这样城堡就能转换成一个图
  • 求房间个数,实际上就是求图中有多少个极大连通子图
  • 一个连通子图往里头加任何一个图里的其他点,都会变得不连通,那么这个连通子图就是极大连通子图
  • 就是自己形成自己一个圈
#include<iostream>
#include<cstring>
using namespace std;
/*问题一,将数字转化为东西南北的墙*/
//题中墙的标准是二进制,利用与运算,判断哪个位有0,就说明可以走
int R,C;//行列数
int rooms[55][55];//存原始房间
int color[55][55];//进行方块标记,识别是否为同一个房间的空间
int maxRoomArea=0,roomNum=0;//最大房间面积和表示第几个房间的记录
int roomArea;//房间面积的记录
void Dfs(int i,int k){//第i排第k列的房间为起点的房间面积
    if(color[i][k]) return;//已经走过了,标记过了
    roomArea++;
    color[i][k]=roomNum;
    if((rooms[i][k]&1)==0) Dfs(i,k-1);//西墙为空
    if((rooms[i][k]&2)==0) Dfs(i-1,k);//北墙为空
    if((rooms[i][k]&4)==0) Dfs(i,k+1);//东墙为空
    if((rooms[i][k]&8)==0) Dfs(i+1,k);//南墙为空
}
int main(){
    cin>>R>>C;
    for(int i=1;i<=R;i++){
        for(int j=1;j<=C;j++){
            cin>>rooms[i][j];
        }
    }
    memset(color,0,sizeof(color));
    for(int i=1;i<=R;i++){
        for(int k=1;k<=C;k++){
            if(!color[i][k]){//标记为0,说明未占这个房间
                roomNum++;
                roomArea=0;
                Dfs(i,k);
                maxRoomArea=max(roomArea,maxRoomArea);
            }
        }
    }
    cout<<roomNum<<endl;
    cout<<maxRoomArea<<endl;
}

例题——踩方格4103

递归思路出发

  • 从(i,j)出发,走n步的方案数,等于以下三项之和:
    • 从(i+1,j)出发,走n-1步的方案数
    • 从(i,j+1)出发,走n-1步的方案数
    • 从(i,j-1)出发,走n-1步的方案数
#include<iostream>
#include<cstring>
using namespace std;
int visited[30][50];
int ways(int i,int j,int n){
    if(n==0) return 1;//步数走完了,这个走法是成立的
    visited[i][j]=1;//标记为走过了
    int num=0;
    if(!visited[i][j-1]) num+=ways(i,j-1,n-1);
    if(!visited[i][j+1]) num+=ways(i,j+1,n-1);
    if(!visited[i+1][j]) num+=ways(i+1,j,n-1);
    visited[i][j]=0;//重新规划新出发的路
    return num;
}
int main(){
    int n;
    cin>>n;
    memset(visited,0,sizeof(visited));
    cout<<ways(0,25,n)<<endl;
}

开始引入最优题——最短,最长等

例题——寻路问题1724

N个城市,编号为1->N

城市间有R条单向道路

每条道路连接两个城市,有长度和过路费两个属性

Bob只有k块钱,他想从城市1走到城市N。

最短共需要走多长的路,如果到不了N,输出-1;//也能延伸出新问题——最少需要多少过路费

即优先级应该是,找短路,短路里找能给的起过路费的

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int K,N,R;
struct Road{
    int d,L,t;
};
vector< vector<Road> > G(110);//因为每个起点可能有很多个终点
int minL[110][10010];//从起点走到i,总花费为j的最优长度
int minLen;//最短路径
int totalLen;//目前这条路的长度
int totalCost;//目前这条路的花费
int visited[110];//标记城市走没走过
void dfs(int s){
    if(s==N){
        minLen=min(minLen,totalLen);
        return;
    }
    for(int i=0;i<G[s].size();i++){
        Road r=G[s][i];
        if(totalCost+r.t>K){
            continue;
        }
        if(!visited[r.d]&&totalLen+r.L<minLen){
            //运用最优性剪枝,减少遍历
            //如果它之前也来过这个路径,而且他上一次来,路径更短,就没必要再往这个路径下去
            if(totalLen+r.L>=minL[r.d][totalCost+r.t]){
                continue;
            }
            minL[r.d][totalCost+r.t]=totalLen+r.L;
            totalLen+=r.L;
            totalCost+=r.t;
            visited[r.d]=1;
            dfs(r.d);
            visited[r.d]=0;
            totalLen-=r.L;
            totalCost-=r.t;
        }
    }
}
int main(){
    cin>>K>>N>>R;
    for(int i=0;i<R;i++){
        int s;
        Road r;
        cin>>s>>r.d>>r.L>>r.t;
        if(s!=r.d){
            G[s].push_back(r);
        }
    }
    memset(visited,0,sizeof(visited));
    totalLen=0;
    minLen=1<<30;//最短长度最开始是一个很大的数
    totalCost=0;
    visited[1]=1;
    for(int i=0;i<110;i++){
        for(int j=0;j<10010;j++){
            minL[i][j]=1<<30;
        }
    }
    dfs(1);
    if(minLen<(1<<30)){
        cout<<minLen<<endl;
    }else{
        cout<<"-1"<<endl;
    }
    return 0;
}

例题——生日蛋糕1190

描述:制作一个体积为NΠ的M层蛋糕,每层都是一个圆柱体

设从下往上数,第i(1<=i<=M)层蛋糕半径是Ri,高度为Hi的圆柱。当i<M时,要求Ri>Ri+1&&Hi>Hi+1

要求蛋糕外表面(最下一层的下底面除外)的面积Q最小

令Q=SΠ

N与M都是整数

深度搜索枚举的本质是,枚举每一层可能的高度和半径

确定搜索范围:底层蛋糕的最大可能半径和最大可能高度

搜索顺序:从底层往上搭蛋糕

如何剪枝提高效率:

#include<iostream>
using namespace std;
int N, M;//体积,层数
int minArea = 1 << 30;//最小面积,初始化为很大的数
inline int min_v(int m)//用于计算搭建剩余蛋糕所需要的最小体积
{//传入的参数m是已搭建的层数
	int v = 0;
	//假设剩余层的半径都是最小值,半径和高度都是1,2,3……M-m
	for (int R = M - m; R >= 1; R--)
	{
		v += R * R * R;
	}
	return v;
}
inline int max_v(int r, int h, int m)
{//传入参数是:m+1层的半径,高度和已经盖了m层
	int v = 0;
	//因为上层半径和高度都必须比下层小,所以每一层半径和高度的最大取值都是下层的半径和高度-1
	for (int R = r, H = h; m <= M; R--, H--, m++)
	{
		v += R * R * H;
	}
	return v;
}
inline void Dfs(int r, int h, int m, int s, int v)
{//搭建该层的半径,高度,层数,目前已用表面积,目前已用体积
	if (m == M && N == v && s < minArea)
	{//层数用完,体积用完,总面积比最小的还小
		minArea = s;
		return;
	}
	if (r == 0 || s + 2 * (N - v) / r > minArea)
	{
		return;
	}//如果预估蛋糕的所用面积已经大于目前确立的最小面积,就剪枝
	if (min_v(m) > N - v)
	{
		return;
	}//剩余层数所需的体积已经大于剩余体积,就说明不够用,根本搭不成
	if (max_v(r, h, m) < N - v)
	{
		return;
	}//剩余层数能达到的最大体积,已经小于剩余体积,说明体积花不完,无法达到预期体积
	for (int R = r - 1; R >= M - m; R--)
	{//最大是r-1,最小也应该有当前层数那么大
		for (int H = h - 1; H >= M-m; H--)
		{
			Dfs(R, H, m + 1, s + 2 * R * H, v + R * R * H);
		}
	}
}
int main() {
	cin >> N >> M;
	for (int r1 = M; r1 * r1 <= N; r1++)
	{
		for (int h1 = N / (r1 * r1); h1 >= M; h1--)
		{
			int s = r1 * r1 + r1 * h1 * 2;
			int v = r1 * r1 * h1;
			Dfs(r1, h1, 1, s, v);
		}
	}
	if (minArea < (1 << 30))
	{
		cout << minArea << endl;
	}
	else
	{
		cout << 0 << endl;
	}
}


天梯校内选拔——金币问题

#include<iostream>
#include<cstring>
using namespace std;
bool visited[105][105];
int rmp[105][105];
void nuit()
{
    rmp[0][0]=0;
    for (int i = 0; i <= 99; i++)
    {
        rmp[i][0] = i / 10 + i % 10;
        for (int j = 1; j <= 99; j++)
        {
            if (j % 10 > 0)
                rmp[i][j] = rmp[i][j - 1] + 1;
            else
                rmp[i][j] = rmp[i][j - 1] - 9 + 1;
        }
    }
    rmp[100][0] = 1;
    rmp[0][100] = rmp[0][99] + 1;
    for (int i = 1; i <= 99; i++)
    {
        if (i % 10 > 0)
            rmp[100][i] = rmp[100][i - 1] + 1;
        else
            rmp[100][i] = rmp[100][i - 1] - 9 + 1;
        rmp[i][100] = rmp[i][99] + 1;
    }
    rmp[100][100] = 2;
}
int cnt=0;
void Dfs(int sx,int sy,int ex,int ey,int k)
{
    if(sx==ex-1 && sy==ey-1)
    {
        cnt++;
        visited[sx][sy]=true;
        return;
    }
    cnt++;
    visited[sx][sy]=true;
    if(sx-1>=0 && !visited[sx-1][sy] && rmp[sx-1][sy]<=k)
        Dfs(sx-1,sy,ex,ey,k);
    if(sy-1>=0 && !visited[sx][sy-1] && rmp[sx][sy-1]<=k)
        Dfs(sx,sy-1,ex,ey,k);
    if(sx+1<ex && !visited[sx+1][sy] && rmp[sx+1][sy]<=k)
        Dfs(sx+1,sy,ex,ey,k);
    if(sy+1<ey && !visited[sx][sy+1] && rmp[sx][sy+1]<=k)
        Dfs(sx,sy+1,ex,ey,k);
}
int main()
{
    int m,n,k;
    cin>>m>>n>>k;
    memset(visited,false,sizeof(visited));
    nuit();
    Dfs(0,0,m,n,k);
    cout<<cnt<<endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值