2021-09-18

各个图论的理论讲解(符代码)

*由于本蒟蒻太弱真的只是一名13岁的新初二学生,所以这里不提SPFA等高级算法,只是概括了:

  1. 克鲁斯卡尔
  2. prim
  3. Dijkstra 地杰斯塔拉(优先队列优化)
  4. Dijkstra 地杰斯塔拉(无优化)
  5. Floyd 弗洛伊德*

1. Kruskal 克鲁斯卡尔
(用于求最小生成树…)

这里先讲一下:最小生成树就是指一颗贯通整个图的树,并且可以从任意一个点到达任意一个点
这个本质就是找到最小生成树然后再求总和
(1).如何寻找最小生成树:
只需找到一个“爸爸”点,然后再往上加,每次和“爸爸”家族连接即可。
程序中的 fa 数组就是用于记录它到底属于哪一个家族。(并且由于这个特性,Kruskal更适用于稀疏图…自己脑补吧…)
代码O(∩_∩)O哈哈~
劲量看懂再抄…

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5+10;
struct node{
    int u,v,w;
}e[2*maxn];
int fa[maxn];  // fa[i] 为顶点 i 所在集合对应的树中的根结点
int r[maxn];
int n,m;   //点和边
void init() {//初始化
    for(int i=0;i<=n;i++)
    {
        fa[i] = i;
        r[i] = 0;
    }
}
bool cmp(node a,node b){
    return a.w<b.w;
}
int find(int x){
    if(fa[x]==x)
        return x;
    else
        return fa[x] = find(fa[x]);
}
void merge(int x,int y){
    x = find(x);//x 和 y 都去找爸爸
    y = find(y);
    if(x!=y){
        if(r[x]<r[y])//这里如果y的家族更大,那么大家都跟着它混
            fa[x]=y;//纯属拼爹......
        else{
            fa[y] = x;
            if(r[x]==r[y])
                r[x]++;//反正y这个x走了,所以就加上呗~
        }
    }
}
void kruskal(){
    int sumw = 0;  //生成树的权值
    int num = 0;  //已选用边的数目
    int u,v;
    init();
    for(int i=0; i<m; i++){
        u = e[i].u;
        v = e[i].v;
        if( find(u)!=find(v) ){//开始和结尾的爹不同了
            sumw += e[i].w;
            num++;
            merge(u,v);//看缘分分家...
        }
        if(num >= n-1)//终于分完了...
            break;
    }
    printf("%d\n",sumw);
}
int main(){
    int t;
    int u,v,w;
    while(~scanf("%d",&n) && n){
        scanf("%d",&m);
        for(int i=0; i<m ;i++){
            scanf("%d%d%d",&u,&v,&w);
            e[i].u = u;
            e[i].v = v;
            e[i].w = w;
        }
        sort(e,e+m,cmp);
        kruskal();
    }
    return 0;

2.Prim//真**的累嗨没办法啊生活真苦…
这个也是求最小生成树的,还没吐的就再忍一忍吧…
这个的大概就是先找好最小生成树然后再一点一点的加上去
这个更适用于稠密图…
代码
尽量看懂再抄~~

#include <bits/stdc++.h>
using namespace std;
int n, q[1001][1001], minn[100001], ans;//minn表示不在最小生成树中的点与在最小生成树中的点相连的最小边权
bool f[100001];//不在最小生成树中的点f等于false,在就等于true
int main() {
	memset(minn, INF, sizeof(minn));//初始化
	minn[1] = 0;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			scanf("%d", &q[i][j]);//输入邻接矩阵
		}//q[i][j]表示从i到j用多久
	}
	for(int i = 1; i <= n; i++) {
		int k = 0;
		for(int j = 1; j <= n; j++) {
			if(!f[j] && minn[j] < minn[k]) { //寻找权值最短的边(且不能是已经在最小生成树中的点)
				k = j;
			}
		}
		f[k] = true;//把它加入最小生成树
		for(int j = 1; j <= n; j++) {
			if(!f[j] && q[k][j] < minn[j]) {
				minn[j] = q[k][j];
			}
		}
	}
	for(int i = 1; i <= n; i++) {
		ans += minn[i];//把所有在最小生成树中的点的权值加起来
	}
	printf("%d", ans);
	return 0;
}

这个代码这段享受
3. Floyd
这个更短,只是时间复杂度有点大。n3
这个就跟最小生成树没关系了~~

#include<bits/stdc++.h>
int main() {
	int e[10][10],k,i,j,n,m,t1,t2,t3;
	int inf=99999999;
	//读入n和m,n表示顶点个数,m表示边的条数
	scanf("%d %d",&n,&m);
	//初始化
	for(i=1; i<=n; i++)
		for(j=1; j<=n; j++)
			if(i==j) e[i][j]=0;
			else e[i][j]=inf;
	//读入边
	for(i=1; i<=m; i++) {
		scanf("%d %d %d",&t1,&t2,&t3);
		e[t1][t2]=t3;
	}
	//Floyd算法核心语句
	for(k=1; k<=n; k++)
		for(i=1; i<=n; i++)
			for(j=1; j<=n; j++)
				if(e[i][j]>e[i][k]+e[k][j] )//意思就是如果在两点之间在加上一个点的话,如果可以更短就加
					e[i][j]=e[i][k]+e[k][j];
	//输出最终的结果
	for(i=1; i<=n; i++) {
		for(j=1; j<=n; j++) {
			printf("%10d",e[i][j]);
		}
		printf("\n");
	}
	return 0;
}

4.Dijkstra(警告:这个最**的烦人,心态不好的不要进入…)
Dijkstra就是求一个从1到i的最短路

给你们看一个视频
Dijkstra详解
2021-09-18
最后再给各位DALAO们讲一下代码

#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int inf=2147483647;
const int maxn=10000+10;
struct hh
{
  int u,v,w,next;
}e[maxn];
int n,m,x,head[maxn],dis[maxn];
bool vis[maxn];//this is weather or not it has been visited
void dij()
{
  int k;
  memset(vis,0,sizeof(vis));
  for(int i=1;i<=n;i++)dis[i]=inf;//dis means the dis[i] from 0 to i
  dis[x]=0;//x means where it starts so that is defintly 0
  for(int i=1;i<=n;i++)
  {
    k=-1;
    for(int j=1;j<=n;j++)
     if(!vis[j]&&(k==-1||dis[j]<dis[k]))k=j;//this is the part where you find the smallist
    vis[k]=true;//been visited
    for(int j=head[k];j!=-1;j=e[j].next)//always looking for the next
     if(!vis[e[j].v]&&e[j].w+dis[k]<dis[e[j].v])dis[e[j].v]=e[j].w+dis[k];//if this hasn't been visited before and the total distance is smaller then just changing it
  }
}
int main()
{
  scanf("%d %d %d",&n,&m,&x);
  for(int i=1;i<=m;i++)
  {
      scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);//start end cost
      e[i].next=head[e[i].u];//cheak out the picture 这个就是前面点
      head[e[i].u]=i;//出发点
  }
  dij();
  for(int i=1;i<=n;i++)
   printf("%d",dis[i]);
  return 0;
}

5.Dijkstra+堆优化(终极警告真烦人,别说我没警告你…)

在这里插入代码片#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int INF = 2147483647;
const int maxn = 10000 + 10;
const int maxm = 500000 + 10;
int n, m, s;
int fir[maxn], nxt[maxm], to[maxm], val[maxm], cnt;
void add_edge(int u, int v, int w) //前向星加边
{
    nxt[++cnt] = fir[u];
    fir[u] = cnt;
    to[cnt] = v;
    val[cnt] = w;
}
struct Node 
{
    int d, id;
    Node(){}
    Node(int d, int id) : d(d), id(id){}
    bool operator < (const Node& rhs) const
      {
        return d > rhs.d;//重载 < 方便堆
      }
};
int dis[maxn], vis[maxn];
void Dijkstra(int s)
{
    for(int i = 1; i <= n; i++) dis[i] = INF;
    dis[s]=0;
    priority_queue<Node> Q;
    //here you can see that it is the same as the code from the top
    //just that priority queue can just sort it from small to large
    //so now it is a bit more faster 
   	//if you are going to memorize Dijkstra then please remember this
    Q.push(Node(0,s));
    while(!Q.empty()) 
      {
        Node u = Q.top(); Q.pop();
        if(vis[u.id]) continue;  //若某个点已经被更新到最优,就不用再次更新其他点
        vis[u.id] = 1;
        for(int e = fir[u.id]; e; e = nxt[e]) 
          {
            int v = to[e], w = val[e];
            if(u.d + w < dis[v]) 
              {
                dis[v] = u.d + w;
                Q.push(Node(dis[v],v));
              }
          }
      }
}
int main()
{
    scanf("%d%d%d",&n ,&m ,&s);
    for(int u, v, w, i=0; i < m; i++)
      {
        scanf("%d%d%d",&u ,&v ,&w);
        add_edge(u, v, w);
      }
    Dijkstra(s);
    for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
    return 0;
}

6.拓扑排序
本蒟蒻目前还不认识拓扑排序,打字时候只能打tppx 并我不知道到底拼音是什么,只好把他叫成脱裤排序…O(∩_∩)O哈哈~
请求大家催更,要不然我都懒得写了,太**的累了o( ̄▽ ̄)d

祝贺大家NOIP,CSP-J,CSP-S,acm RP++!!!

上一篇:2021-5-24 力扣每日一题


下一篇:CF1168A - Increasing by Modulo (贪心+二分)