加载中...

图论基础

深度优先遍历

深度优先遍历(DFS), 指从图中一个未遍历的节点u开始, 沿着一条路一直走到底, 然后从这条路尽头回退到上一个节点, 再从另一条路开始走到底. 不断递归重复此过程, 直到所有节点都遍历完成.


深度优先遍历经典问题Ⅰ 排列数字

#include<iostream>
#include<algorithm>

using namespace std;

const int N=10;
int n;
int path[N];
bool st[N];

void dfs(int u)
{
    if(u==n)
    {
        for(int i=0;i<n;i++)cout<<path[i]<<' ';
        cout<<'\n';
        return;
    }
    
    for(int i=1;i<=n;i++)
        if(!st[i])
        {
            path[u]=i;
            st[i]=true;
            dfs(u+1);
            st[i]=false;	//恢复现场 + 回溯
        }
}

int main()
{
    cin>>n;
    dfs(0);
    return 0;
}

深度优先遍历经典问题Ⅱ n-皇后问题

//第一种搜索顺序(按行搜索)

#include<iostream>
#include<algorithm>

using namespace std;

const int N=20;
int n;
char g[N][N];
bool col[N],dg[N],udg[N];	//col表示列,dg表示“/”对角线,udg表示“\”对角线

void dfs(int u)		//u按行搜索
{
    if(u==n)
    {
        for(int i=0;i<n;i++)cout<<g[i]<<'\n';
        cout<<'\n';
        return;
    }
    
    for(int i=0;i<=n;i++)
        if(!col[i]&&!dg[u+i]&&!udg[n-u+i])
        {
            g[u][i]='Q';
            col[i]=dg[u+i]=udg[n-u+i]=true;
            dfs(u+1);
            col[i]=dg[u+i]=udg[n-u+i]=false;	//恢复现场+回溯
            g[u][i]='.';
        }
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            g[i][j]='.';
    dfs(0);
    return 0;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------

//第二种搜索顺序(按每一个格子搜索)

#include<iostream>
#include<algorithm>

using namespace std;

const int N=20;
int n;
char g[N][N];
bool row[N],col[N],dg[N],udg[N];

void dfs(int x,int y,int s)		//s是皇后个数
{
    if(y==n)y=0,x++;
    if(x==n)
    {
        if(s==n)
        {
            for(int i=0;i<n;i++)cout<<g[i]<<'\n';
            cout<<'\n';
        }
        return;
    }
    
    //不放皇后
    dfs(x,y+1,s);
    //放皇后
    if(!row[x]&&!col[y]&&!dg[x+y]&&!udg[x-y+n])
    {
        g[x][y]='Q';
        row[x]=col[y]=dg[x+y]=udg[x-y+n]=true;
        dfs(x,y+1,s+1);
        row[x]=col[y]=dg[x+y]=udg[x-y+n]=false;		//恢复现场+回溯
        g[x][y]='.';
    }
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            g[i][j]='.';
    dfs(0,0,0);
    return 0;
}



宽度优先遍历

宽度优先遍历(BFS), 又称广度优先遍历, 指从图的一个未遍历的节点出发, 先遍历这个节点的相邻节点, 再依次遍历每个相邻节点的相邻节点.


宽度优先遍历经典题目Ⅰ 走迷宫

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

using namespace std;

const int N=110;
int n,m;
int g[N][N],d[N][N];	
//g[][]用于存储迷宫地图,d[][]用于记录每个点是否被搜索过,存储每一个点到起点的距离

int bfs()
{
    queue<pair<int,int>>q;
    
    memset(d,-1,sizeof d);
    d[0][0]=0;
    q.push({0,0});
    
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};	//偏移量
    
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        
        for(int i=0;i<4;i++)
        {
            int x=t.first+dx[i],y=t.second+dy[i];
            if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]=-1)
            {
                d[x][y]=d[t.first][t.second]+1;
                q.push({x,y});
            }
        }
    }
    
    return d[n-1][m-1];
}

int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            cin>>g[i][j];
    
    cout<<bfs()<<endl;
    return 0;
}

宽度优先遍历经典问题Ⅱ 八数码

#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>

using namespace std;

int bfs(string start)
{
    string end="12345678x";
    
    queue<string>q;
    unordered_map<string,int>d;
    
    q.push(start);
    d[start]=0;
    
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        
        int distance=d[t];
        if(t==end)return distance;
        
        //状态转移
        int k=t.find('x');
        int x=k/3,y=k%3;
        for(int i=0;i<4;i++)
        {
            int a=x+dx[i],b=y+dy[i];
            if(a>=0&&a<3&&b>=0&&b<3)
            {
                swap(t[k],t[a*3+b]);
                if(d.count(t)==0)
                {
                    d[t]=distance+1;
                    q.push(t);
                }
                swap(t[k],t[a*3+b]);
            }
        }
    }
    
    return -1;
}



树与图的存储

数是一种特殊的图(无环连通图)

图分为有向图、无向图

无向图是特殊的有向图, 对无向图中的边ab, 存储两条有向边 a \(\to\) b , b \(\to\) a

\[有向图的存储 \begin{cases} 邻接矩阵(不能处理重边) \\[4ex] 邻接表 \end{cases} \]


int h[N],e[N],ne[N],idx;

//添加一条从a到b的边
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}



树与图的深度优先遍历

void dfs(int u)
{
    st[u]=true;		//表示点u已经被遍历过
    
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!st[j])dfs(j);
    }
}



树与图的宽度优先遍历

queue<int>q;
st[1]=true;		//表示1号点已经被遍历过
q.push(1);

while(q.size())
{
    int t=q.front();
    q.pop();
    
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!st[j])
        {
            st[j]=true;
            q.push(j);
        }
    }
}



拓扑排序

拓扑排序是图的宽度优先搜索的一个应用

拓扑图即有向无环图


//拓扑排序模板
bool topsort()
{
    int hh=0,tt=-1;
    
    //d[i]存储点i的入度
    for(int i=1;i<=n;i++)
        if(d[i]==0)q[++tt]=i;
    
    while(hh<=tt)
    {
        int t=q[hh++];
        
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(--d[j]==0)q[++tt]=j;
        }
    }
    
    //如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列
    return tt==n-1;
}


posted @ 2023-04-28 23:59  邪童  阅读(8)  评论(0编辑  收藏  举报