各个图论的理论讲解(符代码)
*由于本蒟蒻太弱真的只是一名13岁的新初二学生,所以这里不提SPFA等高级算法,只是概括了:
- 克鲁斯卡尔
- prim
- Dijkstra 地杰斯塔拉(优先队列优化)
- Dijkstra 地杰斯塔拉(无优化)
- 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++!!!