图论基础
存图
邻接矩阵
邻接表:
vector
初始化:vector<结构体名> G[10001][21];
读入:q[x][y].push_back((结构体名字){读入的数据})
struct edge {
int to, w;//表示终点,权值。
};
int n, m, k;
vector<int> G[10001];
int main() {
scanf("%d%d%d", &n, &m);//表示点数,边数。
for (int i = 1; i <= m; i++) {
int a, b, c;//代表一个边起点,终点,权值。
scanf("%d%d%d", &a, &b, &c);
G[a].push_back((edge) {b, c});//按照结构体类型强压,变量顺序要按照在结构体中定义的顺序。
}
G[a][i].to; //以a为起点的第i条边终点为to(前面村的对应的c)
G[a][i].w;//同理。
//对于像迪杰斯特拉等需要遍历以某点为起点的我们照样i<=G[a].size即可
return 0;
}
链式前向星
struct ty
{
int t,w,next;
}
int m;//边数
ty edge[N];
int head[N];
void insertEdge(int x,int y,int z)
{
edge[++m].t=x;
edge[m].w=z;
edge[m].next=head[x];
head[x]=m;
}
欧拉回路
无向图:连通且度为偶数
有向图:连通,每个点的入度和出度相同
混合图
拓扑排序
菜肴制作(优先队列的拓扑排序)
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int cnt,n,m;//n为点数,m为边数
struct ty
{
int y,next;
}edge[100010];
int head[100010];
void addedge(int x,int y)
{
edge[++cnt].y=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
int inc[100010];
int ans[100010];
priority_queue<int>q;
bool tuopo()
{
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++)
if(inc[i]==0) q.push(i);
int num=0;
while(!q.empty())
{
int x=ans[++num]=q.top();
q.pop();
for(int i=head[x];i!=-1;i=edge[i].next)
{
int y=edge[i].y;
inc[y]--;
if(inc[y]==0) q.push(y);
}
}
if(num<n) return 0;
else return 1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
//int n,m;
scanf("%d%d",&n,&m);
cnt=0;
memset(head,-1,sizeof(head));
memset(inc,0,sizeof(inc));
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(y,x);
inc[x]++;
}
if(tuopo()==0) cout<<"Impossible!";
else
for(int i=n;i>=1;i--)
cout<<ans[i]<<' ';
cout<<endl ;
}
return 0;
}
拓扑排序可以判断有无环,能拓扑排序的一定没有环
AOE网
带边权的无环图
关键路径
最早时间和最晚时间相同的事件
最短路
单源最短路
松弛操作
dijkstra算法
不含负权边
链式前向星版
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=10010;
struct ty
{
int t,w,next;
};
int head[10010];
ty edge[20010];
int cnt;
void addedge(int x,int y,int z)
{
edge[++cnt].t=y;
edge[cnt].next=head[x];
edge[cnt].w=z;
head[x]=cnt;
}
int dis[10010];
int st[10010];
struct ty2
{
int x,dis;
bool operator < (const ty2 &a)const
{
return dis>a.dis;
}
};
priority_queue<ty2>q;
int dij(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
ty2 tmp;
tmp.x=s,tmp.dis=dis[s];
q.push(tmp);
while(!q.empty())
{
ty2 tmp=q.top();
q.pop();
if(st[tmp.x])continue;
st[tmp.x]=1;
for(int i=head[tmp.x];i!=-1;i=edge[i].next)
{
int y=edge[i].t;
if(st[y])continue;
if(dis[y]>dis[tmp.x]+edge[i].w)
{
dis[y]=dis[tmp.x]+edge[i].w;
ty2 tmp2;
tmp2.x=y;
tmp2.dis=dis[y];
q.push(tmp2);
}
}
}
return dis[t];
}
int main()
{
int n,m,s,t;
cin>>n>>m>>s>>t;
memset(head,-1,sizeof(head));
int a,b,v;
for(int i=1;i<=m;++i)
{
cin>>a>>b>>v;
addedge(a,b,v);
addedge(b,a,v);
}
int ans=dij(s,t);
if(ans>=0x3f3f3f)ans=-1;
cout<<ans<<endl;
return 0;
}
矩阵版
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,s,t;
int g[10010][10010];
int dist[10010];
bool st[10010];
int dijkstra(){
memset(dist,0x3f3f3f,sizeof dist);//初始化为正无穷
dist[s]=0;
for(int j=1;j<=n;++j){
int t=-1;
if(!st[j]&&(t==-1||dist[t]>dist[j])){
t=j;//找出当前未在s中标记且距离源点最近的点
}//先找最小权重的t
st[t]=true;//标记t
for(int j=1;j<=n;++j){
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
return dist[t];
}
int main(){
cin>>n>>m>>s>>t;
memset(g,0x3f3f,sizeof g);
while(m--){
int a,b,c;
cin>>a>>b>>c;//稠密图使用邻接表来表示
g[a][b]=g[b][a]=min(g[a][b],c);//同两个边之间可能存在两条不同权重的边,取权重最小的
}
int tt=dijkstra();
if(tt>=0x3f3f)tt=-1;
cout<<tt;
}
BellmanFord(spfa)
可以有负权边,但不能有负环
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct ty
{
int t,next,w;
};
int n,m,s,t;
int inqueue[10010];
int dist[10010];
int head[10010];
ty edge[20010];
int cnt;
queue<int>q;
void addedge(int x,int y,int z)
{
edge[++cnt].t=y;
edge[cnt].w=z;
edge[cnt].next=head[x];
head[x]=cnt;
}
int spfa(int s,int t)
{
memset(dist,0x3f3f,sizeof(dist));
dist[s]=0;
inqueue[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
inqueue[x]=0;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int y=edge[i].t;
if(dist[y]>dist[x]+edge[i].w)
{
dist[y]=dist[x]+edge[i].w;
if(!inqueue[y])
{
q.push(y);
inqueue[y]=1;
}
}
}
}
return dist[t];
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n>>m>>s>>t;
int a,b,v;
for(int i=1;i<=m;++i)
{
cin>>a>>b>>v;
addedge(a,b,v);
addedge(b,a,v);
}
int ans=spfa(s,t);
if(ans>=0x3f3f3f)ans=-1;
cout<<ans;
return 0;
}
多源最短路
Floyd
void floyd()
{
for(int k=1;k<=n;++k)
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if((i!=j)&&(j!=k)&&(i!=k))
{
if(dist[i][j]>dist[i][k]+dist[k][j])
{
dist[i][j]=dist[i][k]+dist[k][j];
}
}
}
}
}
}
最小生成树
Prim
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int n,m;
int a,b,v;
struct ty
{
int t,next,w;
};
int head[500000+10];
ty edge[2*500000+10];
int cnt=0;
int ans=0;
struct ty2
{
int x,len;
bool operator <(const ty2 &a)const
{
return len>a.len;
}
};
priority_queue<ty2> q;
bool intree[500000+10];
void prim()
{
ty2 tmp;
intree[1]=1;
for(int i=head[1];i!=-1;i=edge[i].next)
{
tmp.x=edge[i].t;
tmp.len=edge[i].w;
q.push(tmp);
}
while(!q.empty())
{
ty2 tmp=q.top();
q.pop();
int x=tmp.x;
if(intree[x])continue;
intree[x]=1;
ans+=tmp.len;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int y=edge[i].t;
if(intree[y])continue;
tmp.x=y;
tmp.len=edge[i].w;
q.push(tmp);
}
}
}
void addedge(int x,int y,int z)
{
edge[++cnt].t=y;
edge[cnt].w=z;
edge[cnt].next=head[x];
head[x]=cnt;
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n>>m;
for(int i=1;i<=m;++i)
{
cin>>a>>b>>v;
addedge(a,b,v);
addedge(b,a,v);
}
prim();
cout<<ans;
return 0;
}
Kruskal
利用并查集
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct ty
{
int x,y,w;
};
ty edge[500010];
bool cmp(ty a,ty b)
{
return a.w<b.w;
}
int fa[500010];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
{
fa[i]=i;
}
for(int i=1;i<=m;++i)
{
cin>>edge[i].x>>edge[i].y>>edge[i].w;
}
sort(edge+1,edge+1+m,cmp);
int ans=0;
for(int i=1;i<=m;++i)
{
int fx=find(edge[i].x);
int fy=find(edge[i].y);
if(fx==fy)continue;
fa[fx]=fy;
ans+=edge[i].w;
}
cout<<ans<<endl;
return 0;
}