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

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

*由于本蒟蒻太弱真的只是一名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详解
请添加图片描述
最后再给各位DALAO们讲一下代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<iostream>
using namespace std;
int head[100000],cnt;
long long ans[1000000];
bool vis[1000000];
int m,n,s;
struct edge {
    
    
	int to;
	int nextt;
	int wei;
} edge[1000000];
void addedge(int x,int y,int z) {
    
    
	edge[++cnt].to=y;
	edge[cnt].wei=z;
	edge[cnt].nextt=head[x];
	head[x]=cnt;
}
int main() {
    
    
	cin>>m>>n>>s;
	for(int i=1; i<=n; i++) {
    
    
		ans[i]=2147483647;
	}
	ans[s]=0;
	for(int i=1; i<=n; i++) {
    
    
		int a,b,c;
		cin>>a>>b>>c;
		addedge(a,b,c);
	}
	int pos=s;
	while(vis[pos]==0) {
    
    
		long long minn=2147483647;
		vis[pos]=1;
		for(int i=head[pos]; i!=0; i=edge[i].nextt) {
    
    
			if(!vis[edge[i].to]&&ans[edge[i].to]>ans[pos]+edge[i].wei) {
    
    
				ans[edge[i].to]=ans[pos]+edge[i].wei;
			}
		}
		for(int i=1; i<=m; i++) {
    
    
			if(ans[i]<minn&&vis[i]==0) {
    
    
				minn=ans[i];
				pos=i;
			}
		}
	}
	for(int i=1; i<=m; i++) {
    
    
		cout<<ans[i]<<' ';
	}
}

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
7.SPFA更新与10.7

#include "bits/stdc++.h"


//what SPFA is trying to do is it will find the first dot and go looking for it's neibores which means dots that have an connection with it
//then it will add pop it's self and add all the neiboring dots to the queue
//next if the new dots can do some help see line 48 then add it to the queue
//and you always have an bool saving what is inside the queue and what is not
//somedot can be inside the queue more the a time but there can't be two of the same dot inside the queue at one time
//if there are a time which one dot is in the queue more than once it's likly that there is a loop or a negtive loop see line 53
//SPFA IS O(E*2)//E MEANS EDGE


using namespace std;
const int maxN=400040 ;
struct Edge {
    
    
	int to , next , w ;

} e[ maxN ];

int n,m,cnt,p[ maxN ],Dis[ maxN ];
int In[maxN ];
bool visited[ maxN ];

void Add_Edge ( const int x , const int y , const int z ) {
    
    
	e[++cnt].to=y ;
	e[cnt].next=p[x];
	e[cnt].w=z;
	p[ x ]=cnt;
	return ;
}

bool Spfa(const int S) {
    
    
	int i,t,temp;
	queue<int> Q;
	memset ( visited , 0 , sizeof ( visited ) ) ;
	memset ( Dis , 0x3f , sizeof ( Dis ) ) ;
	memset ( In , 0 , sizeof ( In ) ) ;

	Q.push ( S ) ;
	visited [ S ] = true ;
	Dis [ S ] = 0 ;
	while( !Q.empty()){
    
    
		t = Q.front();
		Q.pop();
		visited[t]=false;
		for( i=p[t];i;i=e[i].next){
    
    
			temp=e[i].to ;
			if(Dis[temp]>Dis[t]+e[i].w){
    
    
				Dis[temp]=Dis[t]+e[i].w ;
				if( !visited[ temp ] ) {
    
    
					Q.push(temp);
					visited[temp]=true;
					if(++In[temp]>n)return false;
				}
			}
		}
	}
	return true;
}
int main ( ) {
    
    
	int S,T;
	scanf ( "%d%d%d%d" , &n , &m , &S , &T ) ;
	for(int i=1; i<=m ; ++i ) {
    
    
		int x,y,_;
		scanf("%d%d%d",&x,&y,&_ );
		Add_Edge(x,y,_) ;
	}
	if ( !Spfa ( S ) ) printf ( "FAIL!\n" ) ;
	else printf ( "%d\n" , Dis[ T ] 	) ;
	return 0;
}

//SPFA 万岁!! 

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

猜你喜欢

转载自blog.csdn.net/weixin_45446715/article/details/120374125