树与图的遍历
时间复杂度 O(n+m), n表示点数,m表示边数
(1) 深度优先遍历 —— 模板题 AcWing 846. 树的重心
int dfs(int u){ vis[u]=1; int son_size=1,now=0; ee(i,u){ int v=e[i].v; if(!vis[v]){ int t=dfs(v); now=max(now,t); son_size+=t; } } now=max(now,n-son_size); ans=min(ans,now); return son_size; }
(2) 宽度优先遍历 —— 模板题 AcWing 847. 图中点的层次
queue<int>q; inline void spfa(){ mem(dis,0x3f); mem(vis,0); q.push(1); vis[1]=1; dis[1]=0; while(!q.empty()){ int u=q.front();q.pop(); vis[u]=0; ee(i,u){ int v=e[i].v,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(!vis[v]){ vis[v]=1; q.push(v); } } } } }
拓扑排序 —— 模板题 AcWing 848. 有向图的拓扑序列
时间复杂度 O(n+m), n表示点数,m表示边数
queue<int>q; bool topo(){ rep(i,1,n) if(!deg[i]) q.push(i); while(!q.empty()){ int u=q.front();q.pop(); topon[++tot]=u; ee(i,u){ int v=e[i].v; if(--deg[v]==0) q.push(v); } } return tot==n; }
堆优化版dijkstra —— 模板题 AcWing 850. Dijkstra求最短路 II
时间复杂度 O(mlogn), n表示点数,m表示边数
priority_queue<pair<int,int> >q; inline void dij(){ mem(dis,0x3f); mem(vis,0); q.push(make_pair(0,1)); dis[1]=0; while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u])continue; vis[u]=1; ee(i,u){ int v=e[i].v,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; q.push(make_pair(-dis[v],v)); } } } }
Bellman-Ford算法 —— 模板题 AcWing 853. 有边数限制的最短路
时间复杂度 O(nm), n表示点数,m表示边数
#define N 510 #define M 10010 int n,m,k; int dis[N],backup[N]; //使用backup数组的目的是为了防止松弛的次数大于k struct Edge{ int u, v, w; }e[M]; inline int bellman_ford(){ mem(dis,0x3f); dis[1]=0; rep(i,1,k){ memcpy(backup,dis,sizeof(dis)); rep(j,1,m){ int u=e[j].u,v=e[j].v,w=e[j].w; if(backup[u]!=0x3f3f3f3f && dis[v]>backup[u]+w){ dis[v]=backup[u]+w; } } } if(dis[n]==0x3f3f3f3f)return -1; else return dis[n]; } int main(){ rd(n),rd(m),rd(k); rep(i,1,m){ rd(e[i].u),rd(e[i].v),rd(e[i].w); } int t=bellman_ford(); if (t==-1) puts("impossible"); else printf("%d\n", t); return 0; }
spfa 算法(队列优化的Bellman-Ford算法) —— 模板题 AcWing 851. spfa求最短路
时间复杂度 平均情况下 O(m),最坏情况下 O(nm), n表示点数,m表示边数
queue<int>q; inline void spfa(){ mem(dis,0x3f); mem(vis,0); q.push(1); vis[1]=1; dis[1]=0; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; ee(i,u){ int v=e[i].v,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(!vis[v]){ vis[v]=1; q.push(v); } } } } }
spfa判断图中是否存在负环 —— 模板题 AcWing 852. spfa判断负环
时间复杂度是 O(nm), n表示点数,m表示边数
- 不需要初始化dist数组
原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。 - cnt数组表示到达当前这个点最短路的边数,如果cnt[x]>=n,说明至少经过了n条边,即n+1个点,由抽屉原理可知显然有两个点重复,即存在负环
#define N 100010 int head[N],tot,dis[N],cnt[N]; bool vis[N]; struct Edge{ int v,next,w; }e[N<<1]; void add(int u,int v,int w){e[++tot].v=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot;} int n,m,s; queue<int>q; inline bool spfa(int s){ mem(dis,0)//本题可以不做初始化,但加上了也没事,所以就加上呗 mem(vis,0); mem(cnt,0); rep(i,1,n){ vis[i]=1; q.push(i); }////判整个图的负环要将每个节点都加入 while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; ee(i,u){ int v=e[i].v,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; cnt[v]=cnt[u]+1; if(cnt[v]>=n)return 1; if(!vis[v]){ vis[v]=1; q.push(v); } } } } return 0; } int main(){ rd(n),rd(m); rep(i,1,m){ int x,y,z;rd(x),rd(y),rd(z); add(x,y,z); } if(spfa(1)) printf("Yes\n"); else printf("No\n"); return 0; }
floyd算法 —— 模板题 AcWing 854. Floyd求最短路
时间复杂度是 O(n^3)
, n表示点数
/* *如果有负权边,虽然a,b不连通,但之间的距离也会因为负权边的更新小于INF, 因为INF/2也是一个很大的数,所以就用>INF/2来代表不能连通 #define N 210 int dis[N][N]; int n,m,k; inline void floyd(){ rep(k,1,n) rep(i,1,n) rep(j,1,n) dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]); } int main(){ rd(n),rd(m),rd(k); mem(dis,0x3f); rep(i,1,n)dis[i][i]=0;///////////////////////////// rep(i,1,m){ int x,y,z;rd(x),rd(y),rd(z); dis[x][y]=min(dis[x][y],z);/////////////////////// } floyd(); while(k--){ int x,y;rd(x),rd(y); if(dis[x][y]>0x3f3f3f3f/2)printf("impossible\n");/////////////////////// else printf("%d\n",dis[x][y]); } //printf("%d",0x3f3f3f3f); return 0; }
朴素版prim算法 —— 模板题 AcWing 858. Prim算法求最小生成树
时间复杂度是 O(n^2+m), n表示点数,m表示边数
int n; // n表示点数
int g[N][N]; // 邻接矩阵,存储所有边
int dist[N]; // 存储其他点到当前最小生成树的距离
bool st[N]; // 存储每个点是否已经在生成树中
// 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0; for (int i = 0; i < n; i ++ ) { int t = -1; for (int j = 1; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; if (i && dist[t] == INF) return INF; if (i) res += dist[t]; st[t] = true; for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]); } return res;
}
Kruskal算法 —— 模板题 AcWing 859. Kruskal算法求最小生成树
时间复杂度是 O(mlogm), n表示点数,m表示边数
#define N 100010 struct edge{ int u,v,w; bool operator < (const edge &rhs)const{ return w<rhs.w; } }e[N<<1]; int f[N],n,m,ans,cnt; inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);} inline void kruskal(){ sort(e+1,e+m+1); rep(i,1,n)f[i]=i; rep(i,1,m){ int fx=find(e[i].u),fy=find(e[i].v); if(fx==fy)continue; f[fx]=fy; ans+=e[i].w; if(++cnt==n-1)break; } } #undef int int main(){ #define int long long #ifdef WIN32 freopen("","r",stdin); #endif rd(n),rd(m); rep(i,1,m){ rd(e[i].u),rd(e[i].v),rd(e[i].w); } kruskal(); if(cnt<n-1)printf("impossible\n"); else printf("%lld\n",ans); return 0; }
染色法判别二分图 —— 模板题 AcWing 860. 染色法判定二分图
时间复杂度是 O(n+m), n表示点数,m表示边数
#define N 200010 #define M int head[N],tot,col[N],s1,cnt,n,m; bool vis[N]; //col 表示每个点的颜色,-1表示未染色,-2表示白色,1表示黑色 struct edge{ int v,w,next; }e[N<<1]; inline void add(int u,int v){e[++tot].v=v;e[tot].next=head[u];head[u]=tot;} queue<pair<int,int> >q; inline bool bfs(){ col[1]=1; q.push(make_pair(1,1)); while(!q.empty()){ int u=q.front().first; int color=q.front().second; q.pop(); ee(i,u){ int v=e[i].v; if(col[v]==-1){ col[v]=~color; q.push(make_pair(v,col[v])); } else if(col[v]==col[u])return 0; } } return 1; } int main(){ //freopen("erfen.txt","r",stdin); rd(n),rd(m); rep(i,1,m){ int u,v;rd(u),rd(v); add(u,v),add(v,u); } mem(col,-1); if(bfs())printf("Yes\n"); else printf("No\n"); return 0; } /* 4 4 1 3 1 4 2 3 2 4 */ //Yes //col的值有三种,-1(初始值),-2,1;
匈牙利算法 —— 模板题 AcWing 861. 二分图的最大匹配
时间复杂度是 O(nm), n表示点数,m表示边数
#define N 510 #define M 100010 int head[N],cnt; struct edge{ int v,next; }e[M]; inline void add(int u,int v){e[++cnt].v=v;e[cnt].next=head[u];head[u]=cnt;} int match[N]; bool vis[N];//考虑过 int n1,n2,m,ans; inline bool dfs(int u){ ee(i,u){ int v=e[i].v; if(!vis[v]){ vis[v]=1; if(match[v]==0 || dfs(match[v])){ match[v]=u; return 1; } } } return 0; } #undef int int main(){ #define int long long //freopen("erfen.txt","r",stdin); rd(n1),rd(n2),rd(m); while(m--){ int u,v;rd(u),rd(v); add(u,v); } mem(match,0); rep(i,1,n1){ mem(vis,0); if(dfs(i)) ans++; } printf("%lld",ans); return 0; }