图论笔记
全部笔记提示
接下来整理的笔记,有一个很重要的是初始化,如果不初始化,运行以后题目可能不会输出。
这份笔记其实不是非常准确(因为无法在markdown上画图,且不能一步一步讲解),最好还是根据模板一起理解,因为我基本是在解释模板。
我在最后会把老师的讲义放在最后,请对号入座。
并查集笔记
算法解释
建边,由于挨个挨个查找比较麻烦所以要进行【路径压缩】,让每一个节点直接指向根节点。(之所以可以这么做,是因为题目只询问两点是否在同一集合)
代码解释
并查集类型的题主要由4个(包括初始化)函数构成:
- init函数:初始化(最重要的一步,不添加会不输出)。
- find函数:查找这个节点的根节点。
- unite函数:建边,由于可能将两个图(更准确的来说是树)的根节点连到一起这时无论是哪一个节点(下称X)指向另一个节点(下称Y),根据【路径压缩】,指向Y的X其下的子节点会分别指向Y,所以我们要让节点最少的树的根节点指向节点更多的树的根节点。这样时间才会更快。(but,这个连接条件会因题目而异)。
*same函数(因题而异):判断两点是否在一个集合内。
模板代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m;
int fa[N],sz[N];
void init()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
sz[i]=1;
}
}
int find(int x)
{
if(fa[x]!=x)
{
fa[x]=find(fa[x]);
}
return fa[x];
}
void unite(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx==fy) return ;
if(sz[fx]<sz[fy])
{
swap(fx,fy);
}
fa[fy]=fx;
sz[fx]+=sz[fy];
}
bool same(int x,int y)
{
return find(x)==find(y);
}
int main()
{
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>z>>x>>y;
if(z==1)
{
unite(x,y);
}
else
{
if(same(x,y))
{
cout<<"Y"<<"\n";
}
else
{
cout<<"N"<<"\n";
}
}
}
return 0;
}
最小生成树(Kruskal)笔记
算法解释
运用并查集的思想,从小到大排序,若两点不在同一集合便将两点相连。
模板代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,s,t,fa[N],sz[N];
struct ll{
int u,v,sum;
}a[N];
void init()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
sz[i]=1;
}
}
int find(int x)
{
if(fa[x]!=x)
{
fa[x]=find(fa[x]);
}
return fa[x];
}
void unite(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx==fy) return ;
if(sz[fx]<sz[fy])
{
swap(fx,fy);
}
fa[fy]=fx;
sz[fx]+=sz[fy];
}
bool same(int x,int y)
{
return find(x)==find(y);
}
bool cmp(ll x,ll y)
{
return x.sum<y.sum;
}
int main()
{
int cnt=0,ans=-1;
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
cin>>a[i].u>>a[i].v>>a[i].sum;
}
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(!same(a[i].u,a[i].v))
{
unite(a[i].u,a[i].v);
cnt++;
ans+=a[i].sum;
if(cnt==n-1)
{
cout<<ans+1;
return 0;
}
}
}
cout<<"orz";
return 0;
}
Floyd算法笔记
一些废话
个人认为最通俗易懂的图论算法,但由于有三重循环,所以不是什么很好用的算法。
算法解释
定义中转节点 K,若 i->k 能走并且 k->j 能走那么 i->j能走一定成立。当然若本身 i->j 的权值就小,那么就不会更新,也就是取最小值。
模板代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
f[i][j]=INT_MAX;
}
f[i][i]=0;
}
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
f[u][v]=f[v][u]=min(w,f[u][v]);
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(f[i][k]!=INT_MAX&&f[k][j]!=INT_MAX)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<f[i][j]<<" ";
}
cout<<"\n";
}
return 0;
}
/*当你做题很顺时
━━━∑(゚□゚*川)——
*/
传递闭包算法笔记
算法解释
完全基于Floyd算法,没什么好解释的。
模板代码
#include<bits/stdc++.h>
using namespace std;
const int M=3e3+10;
const int N=3e5+10;
int n,m,f[M][M];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>f[i][j];
}
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(f[i][k]&&f[k][j])
{
f[i][j]=1;
}
}
}
}
int ans=-1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<f[i][j]<<" ";
}
cout<<"\n";
}
return 0;
}
/*当你做题很顺时
━━━∑(゚□゚*川)——
*/
Prim算法笔记 Dijkstra(下面简称D)算法笔记
前情提要
我为什么要把Prim划掉呢?(我还找了半天Prim是什么)因为它这个算法很鸡肋,凡是它能做出来的题D也能做,还比它做的好。因此在这里不对Prim进行介绍了。
算法提示
D算法不能处理负环。
算法解释
其实这种算法和bfs非常的像。(然后没了
模板代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int n,m,k,dist[N],tmp[N];
struct node
{
int v;
int w;
};
vector <node> a[N];
void D(int s)
{
priority_queue<pair<int,int> ,vector<pair<int,int> > ,greater<pair<int,int> > >q;
for(int i=1;i<=n;i++)
{
dist[i]=INT_MAX;
}
memset(tmp,0,sizeof(tmp));
dist[s]=0;
q.push({0,s});
while(!q.empty())
{
pair<int,int> p=q.top();
q.pop();
int u=p.second;
if(tmp[u]==1) continue;
tmp[u]=1;
for(auto it:a[u])
{
int v=it.v;
int w=it.w;
if(dist[v]>dist[u]+w)
{
dist[v]=dist[u]+w;
q.push({dist[u]+w,v});
}
}
}
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
{
int v,u,w;
cin>>u>>v>>w;
a[u].push_back({v,w});
}
D(k);
for (int i = 1; i <= n; i++) {
cout << dist[i] << " ";
}
cout<<"\n";
return 0;
}
/*当你做题很顺时
━━━∑(゚□゚*川)——
*/

浙公网安备 33010602011771号