算法基础课之搜索图论

算法基础课之搜索也图论

DFS

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int path[N]; // 存状态
int state[N]; // 数字i是否被用过
int n;
// u 指第一个开始填的位置,从1开始
void dfs(int u)
{
    // 如果填的位数大于n,则输出
    if(u>n)
    {
        for(int i = 1;i<=n;i++)
        {
            cout<<path[i]<<' ';
        }
        cout<<endl;
    }
    // 填数字
    for(int i=1;i<=n;i++)
    {
        // 如果数字未被填入
        if(!state[i])
        {
            path[u]=i; // 填入数字
            state[i]=1; // 状态改变
            dfs(u+1); // 填下一位
            state[i]=0; // 回溯
        }
    }
}
int main()
{
    cin>>n;
    dfs(1);
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 11;
char q[N][N];
bool cor[N*2],dg[N*2],udg[N*2];
int n;
void dfs(int r)
{
    if(r==n)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                cout<<q[i][j];
            }
            cout<<endl;
        }
        cout<<endl;
        return;
    }
    for(int i=0;i<n;i++)
    {
        if(!cor[i]&&!dg[i+r]&&!udg[n-i+r])
        {
            q[r][i]='Q';
            cor[i]=dg[i+r]=udg[n-i+r]=1;
            dfs(r+1);
            cor[i]=dg[i+r]=udg[n-i+r]=0;
            q[r][i]='.';
        }
    }
}
int main()
{
    cin>>n;
    //初始化棋盘状态
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            q[i][j]='.';
        }
    }
    dfs(0);
    return 0;
}

BFS

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
typedef pair<int,int>PII;
int g[N][N];
int d[N][N];
PII q[N*N];
int n,m;
int bfs()
{
    int hh=0,tt=0;
    q[0]={0,0};
    memset(d,-1,sizeof d);
    d[0][0]=0;
    while(hh<=tt)
    {
        PII t=q[tt++];
        int x=
    }
}
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<bits/stdc++.h>
using namespace std;
int bfs(string start)
{
    // 定义最终状态
    string end = "12345678x";
    // 定义队列和dist数组(用哈希表存储)
    queue<string>q;
    unordered_map<string, int>d;
    // 初始化队列和数组
    q.push(start);
    d[start]=0;
    //定义4个方向
    int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
    // 当队列不空时循环
    while(q.size())
    {
        // 取队首元素
        auto t = q.front();
        q.pop();
        int distance = d[t];
        if(t == end) return distance;
        //枚举4个方向
        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)
            {
                // 转移x
                swap(t[k],t[a*3+b]);
                // 保证第一次遍历,避免重复遍历状态,从而保证最少
                if(!d.count(t)) 
                {
                    d[t]=distance+1; // 距离+1
                    q.push(t); // 状态入队 
                }
                swap(t[k],t[a*3+b]); // 恢复状态
            }
        }
    }
    // 否则无法到达状态,输出-1
    return -1;
    
}
int main()
{
    string start;
    for(int i=0;i<9;i++)
    {
        char c;
        cin>>c;
        start+=c;
    }
    cout<<bfs(start)<<endl;
    return 0;
}

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int e[N], ne[N], idx;//邻接表存储图
int h[N];
int q[N], hh = 0, tt = -1;//队列保存入度为0的点,也就是能够输出的点,
int n, m;//保存图的点数和边数
int d[N];保存各个点的入度

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void topsort(){
    for(int i = 1; i <= n; i++){//遍历一遍顶点的入度。
        if(d[i] == 0)//如果入度为 0, 则可以入队列
            q[++tt] = i;
    }
    while(tt >= hh){//循环处理队列中点的
        int a = q[hh++];
        for(int i = h[a]; i != -1; i = ne[i]){//循环删除 a 发出的边
            int b = e[i];//a 有一条边指向b
            d[b]--;//删除边后,b的入度减1
            if(d[b] == 0)//如果b的入度减为 0,则 b 可以输出,入队列
                q[++tt] = b;
        }
    }
    if(tt == n - 1){//如果队列中的点的个数与图中点的个数相同,则可以进行拓扑排序
        for(int i = 0; i < n; i++){//队列中保存了所有入度为0的点,依次输出
            cout << q[i] << " ";
        }
    }
    else//如果队列中的点的个数与图中点的个数不相同,则可以进行拓扑排序
        cout << -1;//输出-1,代表错误
}


int main(){
    cin >> n >> m;//保存点的个数和边的个数
    memset(h, -1, sizeof h);//初始化邻接矩阵
    while (m -- ){//依次读入边
        int a, b;
        cin >> a >> b;
        d[b]++;//顶点b的入度+1
        add(a, b);//添加到邻接矩阵
    }
    topsort();//进行拓扑排序
    return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int g[N][N]; //稠密图用领接矩阵存
int dist[N]; //用于存储每个点到第一个点的距离
bool st[N];//用于确定每个点是否在最短路径中
int n,m; 
int Dijkstra()
{
	memset(dist,0x3f3f3f3f,sizeof(dist));//初始化距离为无穷大
	dist[1]=0;//第一个点从1开始存储,自身到自身的距离为0
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(t==-1||dist[t]>dist[j]))
			{
				t=j;
			}
		}
		st[t]=true;
		for(int j=1;j<=n;j++)
		{
			dist[j]=min(dist[j],dist[t]+g[t][j]);
		}
	 }
	 if(dist[n]==0x3f3f3f3f) return -1;
	 return dist[n]; 
 } 
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	memset(g,0x3f3f3f3f,sizeof(g));
	while(m--)
	{
		int x,y,z;
		cin>>x>>y>>z;
		g[x][y]=min(g[x][y],z);//如果发生重边,需要保留最短的一条边
		 
	}
	cout<<Dijkstra()<<endl;   
	return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
const int N = 1000010;
const int M = 1000010;
int n,m;
//稀疏图用邻接表来存
int h[N];//存储第i个结点的头结点的下标 
int e[M];//存结点i的值,有向边的终点 
int ne[M];//存储结点i的下一个结点的下标
int idx;//存储当前可用的结点下标
int w[M];//存储权值
bool st[N];//记录该点是否在最短路径中 
int dist[N];//存储最短路径 
//memset(h,-1,sizeof h);
//C++中,全局域只能声明、初始化变量; 不能用于赋值、运算、调用函数
//插入一条有向边a --> b,权值为c
void add(int a,int b, int c)
{
	w[idx]=c;
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
 } 
//小根堆优化版的Dijkstra算法
int Dijkstra()
{
	//先初始化 
	memset(dist,0x3f3f3f3f,sizeof(dist));
	dist[1]=0;
	//定义小根堆heap 
	priority_queue<PII,vector<PII>,greater<PII>>heap;
	//插入第一个点
	heap.push({0, 1});
	while(heap.size())
	{
		PII k = heap.top();//取栈顶元素,最小值 
		heap.pop();//出栈
		int ver = k.second, distance=k.first;
		if(st[ver]) continue;//如果终点元素在最短路径中,则不必进行下面操作
		st[ver]=true;
		//从终点开始循环 
		for(int i=h[ver];i!=-1;i=ne[i])
		{
			int j = e[i];//i只是个下标,e[i]中存的是i这个下标对应的值
			if(dist[j]>distance+w[i])
			{
				dist[j]=distance+w[i];
				heap.push({dist[j],j});
			 } 
		 } 
		
	 }
	 if(dist[n]==0x3f3f3f3f) return -1;
	 else return dist[n]; 
 } 
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	//第一件事就是初始化头结点
	memset(h,-1,sizeof h);
	cin>>n>>m;
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
	 } 
	cout<<Dijkstra()<<endl; 
	
	return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
const int M = 10010;
int n,m,k;
int dist[N];
int last[N];
//定义结构体
struct Edge{
    int a,b,c;
}edges[M];
//bellman_ford函数
void bellman_ford()
{
    //第一步,初始化dist数组为无穷大
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++){
        memcpy(last,dist,sizeof dist);
        //循环m条边
        for(int j=0;j<m;j++)
        {
            //存储,转换
            Edge e = edges[j];
            dist[e.b]=min(dist[e.b],last[e.a]+e.c);
        }
    }
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        edges[i]={a,b,c};
    }
    bellman_ford();
    if(dist[n]>0x3f3f3f3f/2) cout<<"impossible"<<endl;
    else cout<<dist[n]<<endl;
    return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef pair<int ,int>PII;
int h[N];
int e[N];
int ne[N];
int w[N];
int idx,n,m;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    w[idx]=c;
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
int spfa()
{
    //第一步初始化dist
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    //第二步,定义队列
    queue<PII>q;
    q.push({0,1});
    st[1]=true;
    //第三步循环
    while(q.size())
    {
        PII p= q.front();
        q.pop();
        int t =p.second;
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j= e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j])
                {
                    st[j]=true;
                    q.push({dist[j],j});
                }
            }
        }
    }
    return dist[n];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int res = spfa();
    if(res==0x3f3f3f3f) cout<<"impossible"<<endl;
    else cout<<res<<endl;
    return 0;
}

在这里插入图片描述

/*
dijkstra 算法 用于求权值为非负的最短路径
朴素的dijkstra算法 o(n^2)
堆优化的dijkstra算法 o(mlogn)
bellman_ford算法用于有负权回路,且存在最多路径k的限制的最短路径 o(nm) 
spfa算法可用于边权值为负,但不能存在负权回路的最短路径 一般O(m),最坏O(nm) 
*/
/*
 Dijkstra算法中的st数组保存的是当前确定了到源点距离最小的点,且一旦确定了最小那么就不可逆了(不可标记为true后改变为false);SPFA算法中的st数组仅仅只是表示的当前发生过更新的点,且spfa中的st数组可逆(可以在标记为true之后又标记为false)。顺带一提的是BFS中的st数组记录的是当前已经被遍历过的点
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef pair<int,int>PII;//到源点的距离,下标号
int h[N];
int e[N];
int w[N];
int ne[N];
int idx=0;
int dist[N];
int cnt[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
	w[idx]=c;
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
 } 
bool spfa(){
	//定义队列 q
	queue<PII>q;
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
    //所有点都入队
	for(int i=1;i<=n;i++)
	{
		st[i]=true;
		q.push({dist[i],i});
	 } 
	while(q.size())
	{
		PII p=q.front();
		q.pop();
		int t=p.second;
		//从队列取出之后该节点st被标记为false,代表之后该节点如果发生更新可再次入队 
		st[t]=false;
		//从t开始循环 
		for(int i=h[t];i!=-1;i=ne[i])
		{
			//取新点j 
			int j=e[i];
			if(dist[j]>dist[t]+w[i])
			{
				dist[j]=dist[t]+w[i];
				cnt[j]=cnt[t]+1;
				if(cnt[j]>=n) return true;
				//若j为更新,则更新,入队 
				if(!st[j])
				{
					st[j]=true;
					q.push({dist[j],j});
				}
			}
		 } 
	 }
	return false; 
	 
}

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	memset(h,-1,sizeof h);
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
	}
	if(spfa()) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	
	return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 210;
const int INF = 1e9;
int d[N][N];
int n,m,k;
void floyd()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
            }
        }
    }
}
int main()
{
    cin>>n>>m>>k;
    //初始化d[i][j]数组为无穷大
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j) d[i][j]=0;
            else d[i][j]=INF;
        }
    }
    while(m--)
    {
        int x,y,z;
        cin>>x>>y>>z;
        d[x][y]=min(d[x][y],z);//考虑有重边的情况
    }
    floyd();
    while(k--)
    {
        int x,y;
        cin>>x>>y;
        if(d[x][y]>INF/2) cout<<"impossible"<<endl;
        else cout<<d[x][y]<<endl;
    }
    return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 510,INF = 0x3f3f3f3f;
int g[N][N];//存无向边
int dist[N];//存i点到集合的最短距离
bool st[N];//判断i点是否在最短生成树的集合中
int n,m; 
int prim()
{
	//第一步初始化dist数组为无穷大
	memset(dist,0x3f,sizeof dist);
	//将第一个点的距离设为0
	dist[1]=0;
	int res=0;//res用于记录最小生成树的长度
	//循环点次,用于将每个点都加在最小生成树中
	for(int i=0;i<n;i++)
	{
		int t=-1;//用于记录新点
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(t==-1||dist[t]>dist[j]))
			{
				t=j;
			}
		 }
		 if(dist[t]==INF) return INF;//直接不连通,没有最小生成树,直接返回不必做下面的操作
		 //加入最小生成树路径和中 
		 res+=dist[t];
		 //点加入集合中 
		 st[t]=true;
		 //更新各点到新集合的最短距离,dist和g数组都是从1开始存储 
		 for(int j=1;j<=n;j++)
		 {
		 	dist[j]=min(dist[j],g[t][j]);
		  } 
	 }
	 return res; 
 } 
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	//第一步,初始化无向图为无穷大
	memset(g,0x3f,sizeof g);
	//第一步输入点数和边数
	cin>>n>>m;
	//输入m条边
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		//因为是无向图且可能有重边 
		g[a][b]=g[b][a]=min(g[a][b],c); 
	 }
	int t= prim();
	if(t==INF) cout<<"impossible"<<endl;
	else cout<<t<<endl; 
	
	return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
//因为是无向图,所以是有M
const int N = 100010,M=200010,INF=0x3f3f3f3f;
int n,m;
int p[N];//存前项节点
//定义数据结构存储边
struct Edge{
    int a,b,w;
    bool operator< (const Edge &W)const
    {
        return w<W.w;
    }
}edges[M];
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);//这一步递归了
    return p[x];
}
int kruskal()
{
    //第一步排序
    sort(edges,edges+m);
    //第二步初始化并查集
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
    }
    int res=0,cnt=0;
    for(int i=0;i<m;i++)
    {
       int a=edges[i].a,b=edges[i].b,w=edges[i].w;
       a=find(a),b=find(b);
       if(a!=b){
           p[a]=b;
           res+=w;
           cnt++;
       }
    }
    if(cnt<n-1) return INF;
    else return res;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        edges[i]={a,b,w};
    }
    int t=kruskal();
    if(t==INF) cout<<"impossible"<<endl;
    else cout<<t<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值