搜索与图论--Dijkstra/拓扑排序(C++)

目录

1.Dijkstra求最短路

输入格式

输出格式

数据范围

输入样例:

输出样例:

代码展示

邻接矩阵法

链式前向星法 

链式前向星法(小根堆优化版)

数据范围

2.拓扑排序

输入格式

输出格式

数据范围

输入样例:

输出样例:

代码展示 

链式前向星法

STL法


1.Dijkstra求最短路

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1。

数据范围

1≤n≤500
1≤m≤10^5
图中涉及边长均不超过10000。

输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
代码展示
邻接矩阵法
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
void Dijkstra()
{
	memset(dist,0x3f,sizeof(dist));
	dist[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[j]<dist[t]))
			t=j;
		}
		st[t]=1;
		for(int j=1;j<=n;j++)
		dist[j]=min(dist[j],dist[t]+g[t][j]);
	}
}
int main()
{
	cin>>n>>m;
	memset(g,0x3f,sizeof(g));
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b]=min(g[a][b],c);
	}
	Dijkstra();
	if(dist[n]!=0x3f3f3f3f)
	cout<<dist[n];
	else
	cout<<-1;
}
链式前向星法 
#include<bits/stdc++.h>
using namespace std;
const int N = 510, M = 100010;
struct node{
	int e,w,ne;
}edge[M];
int h[M],idx;
bool st[N];
int dist[N];
int n,m;
void add(int a,int b,int c)
{
	edge[idx].e=b,edge[idx].w=c,edge[idx].ne=h[a],h[a]=idx++;
}
void Dijkstra()
{
	memset(dist,0x3f,sizeof(dist));
	dist[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[j]<dist[t]))
			t=j;
		}
	st[t]=1;
	for(int j=h[t];j!=-1;j=edge[j].ne)
	{
		int i=edge[j].e;
		dist[i]=min(dist[i],dist[t]+edge[j].w);
	}
	}
}
int main()
{
	memset(h,-1,sizeof(h));
	cin>>n>>m;
	while(m--)
	{
		int a,b,w;
		cin>>a>>b>>w;
		add(a,b,w);
	}
	Dijkstra();
	if(dist[n]!=0x3f3f3f3f)
	cout<<dist[n];
	else
	cout<<-1;
}

链式前向星法(小根堆优化版)

数据范围

1≤n,m≤1.5×10^5
图中涉及边长均不小于 0,且不超过 10000。
数据保证:如果最短路存在,则最短路的长度不超过 10^9。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e6+10;
struct node{
	int e,w,ne;
}edge[N];
int h[N],idx;
bool st[N];
int dist[N];
int n,m;
void add(int a,int b,int c)
{
	edge[idx].e=b,edge[idx].w=c,edge[idx].ne=h[a],h[a]=idx++;
}
void Dijkstra()
{
	memset(dist,0x3f,sizeof(dist));
	dist[1]=0;
	priority_queue<PII,vector<PII>,greater<PII>> heap;
	heap.push({0,1});
	while(heap.size())
	{
		auto t=heap.top();
		heap.pop();
		int ver=t.second,dis=t.first;
		if(st[ver])
		continue;
		st[ver]=1;
		for(int j=h[ver];j!=-1;j=edge[j].ne)
		{
			int i=edge[j].e;
			if(dist[i]>dist[ver]+edge[j].w)
			{
				dist[i]=dist[ver]+edge[j].w;
				heap.push({dist[i],i});
			}
		}
	}
}
int main()
{
	memset(h,-1,sizeof(h));
	cin>>n>>m;
	while(m--)
	{
		int a,b,w;
		cin>>a>>b>>w;
		add(a,b,w);
	}
	Dijkstra();
	if(dist[n]!=0x3f3f3f3f)
	cout<<dist[n];
	else
	cout<<-1;
}

 找到t的耗时从O(n^2)变为O(n),更新dist的耗时从O(m)变为O(m*logn),总时间复杂度从O(n^2)变为O(n+m*logn)...

2.拓扑排序

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。

若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。

输出格式

共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。

否则输出 −1。

数据范围

1≤n,m≤10^5

输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
代码展示 
链式前向星法
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
struct code{
	int e,ne;
}edge[N];
int idx;
int h[N];
int d[N];
queue<int> q;
int n,m;
int ans[N],cnt;
void add(int a,int b)
{
	edge[idx].e=b,edge[idx].ne=h[a],h[a]=idx++;
}
void topsort()
{
	for(int i=1;i<=n;i++)
	{
		if(d[i]==0)
		q.push(i);
	}
	while(q.size())
	{
		int t=q.front();
		q.pop();
		ans[cnt++]=t;
		for(int i=h[t];i!=-1;i=edge[i].ne)
		{
			int x=edge[i].e;
			d[x]--;
			if(d[x]==0)
			q.push(x);
		}
	}
	if(cnt==n)
	for(int i=0;i<cnt;i++)
	cout<<ans[i]<<" ";
	else
	cout<<-1;
}
int main()
{
	cin>>n>>m;
	memset(h,-1,sizeof(h));
	while(m--)
	{
		int x,y;
		cin>>x>>y;
		d[y]++;
		add(x,y);
	}
	topsort();
}
STL法
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
vector<int> h[N];
int d[N];
queue<int> q;
int n,m;
int ans[N],cnt;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		h[x].push_back(y);
		d[y]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(!d[i])
		q.push(i);
	}
	while(q.size())
	{
		int t=q.front();
		q.pop();
		ans[cnt++]=t;
		for(auto x:h[t])
		{
			d[x]--;
			if(d[x]==0)
			q.push(x);
		}
	}
	if(cnt==n)
	for(int i=0;i<cnt;i++)
	cout<<ans[i]<<" ";
	else
	cout<<-1;
}

wwwww

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值