算法小总结-图论
拓扑排序
[HNOI2015]菜肴制作
//
// Created by fxz on 2024/8/3.
//
#include <bits/stdc++.h>
using namespace std;
int ans[1008611];
#define int long long
bool TopSort(vector<vector<int>> &G, int n, vector<int> &inDegree) {
int cnt=1;
int num = 0; //记录加入拓扑排序的顶点数
priority_queue<int> q;
for (int i = 1; i <=n; i++)
if (inDegree[i] == 0)
q.push(i); //将所有入度为0的顶点入队
while (!q.empty()) {
int u = q.top(); //取队首顶点u
ans[cnt++]=u;
q.pop();
for (int i = 0; i <G[u].size(); i++) {
int v = G[u][i]; //u的后继节点
inDegree[v]--; //v的入度减1
if (inDegree[v] == 0) //顶点v的入度减为0则入队
q.push(v);
}
G[u].clear(); //清空顶点u的所有出边
num++;
}
if (num == n) //加入拓扑序列的顶点数为n,说明拓扑排序成功,否则,失败
return true;
else
return false;
}
int32_t main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--) {
int n,m;
cin >> n >>m;
vector<vector<int>> G(n+1);
for (int i = 1; i <= m; ++i) {
int a, b;
cin >> a >> b;
G[b].push_back(a);
}
vector<int> inDegree(n+1);
for (auto x : G) {
for (auto y : x)
inDegree[y]++;
}
bool res = TopSort(G, n, inDegree);
if(res==0)cout<<"Impossible!"<<'\n';
else {
for(int i=n;i>=1;i--){
cout<<ans[i]<<' ';
}
cout<<'\n';
}
}
return 0;
}
Dijkstrra
单源最短路,有向图,无向图都可以用,但是必须是无负权边,每个点都会看一遍
模版如下
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1010;
const int M=200010;
int head[N];
int dis[N];
bool vis[N];
int tot;
int n,m,s,t;
struct ty{
int t,l,next;
}edge[200010];
void add(int x,int y,int z){
edge[++tot].l=z;
edge[tot].t=y;
edge[tot].next=head[x];
head[x]=tot;
}
struct ty2 {
int x, dis;
bool operator<(const ty2 &a) const{
return dis>a.dis;
}
};
priority_queue<ty2> q;
void dij(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof (vis));
dis[s]=0;
ty2 tmp;
tmp.x=s,tmp.dis=0;
q.push(tmp);
while(q.size())
{
ty2 tmp=q.top();
q.pop();
if(vis[tmp.x])continue;
vis[tmp.x]=1;
for(int i=head[tmp.x];i!=-1;i=edge[i].next){
int y=edge[i].t;
if(dis[y]>dis[tmp.x]+edge[i].l){
dis[y]=dis[tmp.x]+edge[i].l;
ty2 tmp2;
tmp2.x=y,tmp2.dis=dis[y];
q.push(tmp2);
}
}
}
if(dis[t]>=0x3f3f3f3f)cout<< -1<<'\n';
else
cout<<dis[t]<<'\n';
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++)
{
int x,y,v;
cin>>x>>y>>v;
add(x,y,v);
add(y,x,v);
}
dij(s,t);
return 0;
}
链式前向星
如果说邻接表是不好写但效率好,邻接矩阵是好写但效率低的话,前向星就是一个相对中庸的数据结构。前向星固然好写,但效率并不高。而在优化为链式前向星后,效率也得到了较大的提升。虽然说,世界上对链式前向星的使用并不是很广泛,但在不愿意写复杂的邻接表的情况下,链式前向星也是一个很优秀的数据结构。 ——摘自《百度百科》
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1010;
const int M=20010;
int head[N],e[M],ne[M],w[M];
int dis[N];
bool vis[N];
int tot;
int n,m,s,t;
int x,y,v;
void add(int x,int y,int z)
{
e[++tot]=y;
w[tot]=z;
ne[tot]=head[x];
head[x]=tot;
}
void dij(int s)
{
priority_queue<pii> p;
dis[s]=0;
p.push({0,s});
while(p.size())
{
int x=p.top().second;
p.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=ne[i])
{
int y=e[i];
int z=w[i];
if(dis[y]>dis[x]+z)
{
dis[y]=dis[x]+z;
p.push({-dis[y],y});
}
}
}
}
int main()
{
memset(dis,0x3f,sizeof(dis));
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++)
{
cin>>x>>y>>v;
add(x,y,v);
add(y,x,v);
}
dij(s);
if(dis[t]>=0x3f3f3f3f) cout<<-1<<endl;
else cout<<dis[t]<<endl;
return 0;
}
Bellman-Ford算法 (需要掌握链式前向星)
暴力做法,解决负权边,但是不能有负环
# include <iostream>
# include <cstdio>
# include <cmath>
# include <cstring>
using namespace std;
# define int long long
# define N 10005
# define M 10005
int s,t,n,m,m2;
double f[N];
struct node{
int x,y;
}a[N];
struct node2{
int to,next;
double w;
}e[M];
int adj[N];
void add(int u,int v,double w2){
m2++;
e[m2].to=v;
e[m2].w=w2;
e[m2].next=adj[u];
adj[u]=m2;
return ;
}
void relax(int u,int v,double w2){
if (f[v]>f[u]+w2){
f[v]=f[u]+w2;
}
return ;
}
void ford(){
memset(f,0x7f7f,sizeof(f));
f[s]=0;
for (int i=1;i<=n-1;i++){
for (int j=1;j<=n;j++){
for (int k=adj[j];k;k=e[k].next){
int l=e[k].to;
relax(j,l,e[k].w);
}
}
}
return ;
}
signed main(){
ford();
printf("%.2lf",f[t]);
return 0;
}
SPFA(贝尔曼-福特算法的优化算法)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=10010;
const int M=200010;
int head[N];
int dis[N];
bool vis[N];
int tot;
int n,m,s,t;
struct ty{
int t,l,next;
}edge[200010];
void add(int x,int y,int z){
edge[++tot].l=z;
edge[tot].t=y;
edge[tot].next=head[x];
head[x]=tot;
}
struct ty2 {
int x, dis;
bool operator<(const ty2 &a) const{
return dis>a.dis;
}
};
queue<int>q1;
void spfa(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof (vis));
dis[s]=0;
vis[s]=1;
q1.push(s);
while(q1.size()){
int x=q1.front();
q1.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].t;
if(dis[y]>dis[x]+edge[i].l){
dis[y]=dis[x]+edge[i].l;
if(!vis[y]){
q1.push(y);
vis[y]=1;
}
}
}
}
if(dis[t]>=0x3f3f3f3f)cout<<-1<<'\n';
else
cout<<dis[t]<<'\n';
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++)
{
int x,y,v;
cin>>x>>y>>v;
add(x,y,v);
add(y,x,v);
}
spfa(s,t);
return 0;
}
Floyd
typedef struct
2 {
3 char vertex[VertexNum]; //顶点表
4 int edges[VertexNum][VertexNum]; //邻接矩阵,可看做边表
5 int n,e; //图中当前的顶点数和边数
6 }MGraph;
7
8 void Floyd(MGraph g)
9 {
10 int A[MAXV][MAXV];
11 int path[MAXV][MAXV];
12 int i,j,k,n=g.n;
13 for(i=0;i<n;i++)
14 for(j=0;j<n;j++)
15 {
16 A[i][j]=g.edges[i][j];
17 path[i][j]=-1;
18 }
19 for(k=0;k<n;k++)
20 {
21 for(i=0;i<n;i++)
22 for(j=0;j<n;j++)
23 if(A[i][j]>(A[i][k]+A[k][j]))
24 {
25 A[i][j]=A[i][k]+A[k][j];
26 path[i][j]=k;
27 }
28 }
29 }