图论:
拓扑排序
并查集
最大二分匹配(匈牙利算法)
二分图最大权匹配(KM算法)
有向图的强连通分量
最短路径算法:(dijkstra, 优先队列优化dijkstra, bellman-ford, SPFA)
Floyd算法
最小生成树:prim
次小生成树
- /*
- * 拓扑排序;
- * 排序的结果从小到大, G[i][j]存储i-j的关系,(0无关系, 1表示存在关系)
- * d[i]表示i节点的入度数;
- */
- int n, G[maxn][maxn], topo[maxn], d[maxn];
- int toposort(){
- int flag = 1; // flag = 1: 有序, flag = -1: 结果不唯一;
- memset(d, 0, sizeof(d));
- for(int i = 0; i < n; ++i)
- for(int j = 0; j < n; ++j) d[i]+=G[j][i];
- for(int k = 0; k < n; ++k){
- int num = 0, p = -1;
- for(int i = 0; i < n; ++i) if(d[i] == 0) { num++; p = i; } // 如果需要顺序输出, 遇到入度为0,直接退出;
- if(num == 0) return 0; // 有环;
- if(num > 1) flag = -1;
- topo[k] = p;
- d[p] = -1;
- for(int i = 0; i < n; ++i)
- if(G[p][i] == 1) d[i]--;
- }
- return flag;
- }
- /*
- *并查集
- *时间复杂度O(M*Alpha(N)) M次查找, 近似为O(M);
- */
- // 并查集 注意初始化for(int i = 0; i < n; ++i) p[i] = i;
- int Find(int x){
- return x == p[x] ? x : p[x] = Find(p[x]);
- }
- void Union(int x, int y){
- int xRoot = Find(x), yRoot = Find(y);
- p[xRoot] = yRoot;
- }
- /*
- * 匈牙利算法;
- */
- int n, k, G[maxn][maxn], linker[maxn];
- bool used[maxn];
- bool dfs(int u){
- for(int v = 0; v < n; ++v){
- if(G[u][v] && !used[v]){
- used[v] = true;
- if(linker[v] == -1 || dfs(linker[v])){
- linker[v] = u;
- return true;
- }
- }
- }
- return false;
- }
- int hungary(){
- int res = 0;
- memset(linker, -1, sizeof(linker));
- for(int u = 0; u < n; ++u){
- memset(used, 0, sizeof(used));
- if(dfs(u)) res++;
- }
- return res;
- }
- /*
- * KM算法
- * 复杂度O(N^3);
- */
- int n, G[maxn][maxn], linker[maxn], lx[maxn], ly[maxn];
- int slack[maxn];
- bool visx[maxn], visy[maxn];
- bool dfs(int x){
- visx[x] = true;
- for(int y = 0; y < n; ++y){
- if(visy[y]) continue;
- int tmp = lx[x]+ly[y]-G[x][y];
- if(tmp == 0){
- visy[y] = true;
- if(linker[y] == -1 || dfs(linker[y])){
- linker[y] = x;
- return true;
- }
- }
- else if(slack[y] > tmp) slack[y] = tmp;
- }
- return false;
- }
- int KM(){
- memset(linker, -1, sizeof(linker));
- memset(ly, 0, sizeof(ly));
- for(int i = 0; i < n; ++i){
- lx[i] = -INF;
- for(int j = 0; j < n; ++j)
- if(G[i][j] > lx[i]) lx[i] = G[i][j];
- }
- for(int x = 0; x < n; ++x){
- for(int i = 0; i < n; ++i) slack[i] = INF;
- while(true){
- memset(visx, 0, sizeof(visx));
- memset(visy, 0, sizeof(visy));
- if(dfs(x)) break;
- int delta = INF;
- for(int i = 0; i < n; ++i)
- if(!visy[i] && delta > slack[i]) delta = slack[i];
- for(int i = 0; i < n; ++i)
- if(visx[i]) lx[i]-=delta;
- for(int i = 0; i < n; ++i)
- if(visy[i]) ly[i]+=delta;
- else slack[i] -= delta;
- }
- }
- int res = 0;
- for(int i = 0; i < n; ++i){
- if(linker[i] != -1) res += G[linker[i]][i];
- }
- return res;
- }
有向图的强连通分量
- /*
- *Tarjan算法
- *复杂度O(N+M);
- */
- const int maxn = 10005; // 点数;
- const int maxm = 100005; // 边数
- int head[maxn], tot;
- int low[maxn], dfn[maxn], Stack[maxn], Belong[maxn]; // Belong数组的数值为1~scc;
- int Index, top, scc; // scc表示强连通分量的个数;
- bool Instack[maxn];
- int num[maxn]; // 各个强连通分量包含点的个数;
- struct Edge{
- int to, next;
- }edge[maxm];
- void addedge(int u, int v){
- edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++;
- }
- void Tarjan(int u){
- int v;
- low[u] = dfn[u] = ++Index;
- Stack[top++] = u;
- Instack[u] = true;
- for(int i = head[u]; i != -1; i = edge[i].next){
- v = edge[i].to;
- if(!dfn[v]){
- Tarjan(v);
- low[u] = min(low[u], low[v]);
- } else if(Instack[v]) low[u] = min(low[u], dfn[v]);
- }
- if(low[u] == dfn[u]){
- scc++;
- do{
- v = Stack[--top];
- Instack[v] = false;
- Belong[v] = scc;
- num[scc]++;
- } while(v!=u);
- }
- }
- void solve(){
- memset(dfn, 0, sizeof(dfn));
- memset(Instack, false, sizeof(Instack));
- memset(num, 0, sizeof(num));
- Index = scc = top = 0;
- for(int i = 0; i < n; ++i){
- if(!dfn[i])
- Tarjan(i);
- }
- }
- void init(){
- memset(head, -1, sizeof(head));
- tot = 0;
- }
最短路径算法:
- // dijkstra 模板; O(N^2)
- int g[maxn][maxn], dist[maxn], n; //dist保存单源最短路径;
- bool vis[maxn];
- void dijkstra(int u){ // u表示起始位置;
- for(int i = 0; i < n; ++i) dist[i] = INF, vis[i] = false;
- dist[u] = 0;
- for(int j = 0; j < n-1; ++j){
- int k = -1;
- int Min = INF;
- for(int i = 0; i < n; ++i)
- if(!vis[i] && dist[i] < Min){ Min = dist[i]; k = i; }
- if(k == -1) break;
- vis[k] = true;
- for(int i = 0; i < n; ++i)
- if(!vis[i] && dist[k]+g[k][i]<dist[i])
- dist[i] = dist[k]+g[k][i];
- }
- }
- /*
- * 使用优先队列优化Dijkstra算法;
- * 复杂度 O(ElogE)
- */
- int dist[maxn], n;
- struct qnode{
- int v, d;
- qnode(int _v, int _d):v(_v), d(_d){}
- bool operator < (const qnode &rhs)const{
- return d > rhs.d;
- }
- };
- struct Edge{
- int v, w;
- Edge(int _v, int _w):v(_v), w(_w){}
- };
- vector<Edge> G[maxn];
- void dijkstra(int u){
- priority_queue<qnode> pq;
- for(int i = 0; i < n; ++i) dist[i] = INF;
- dist[u] = 0;
- pq.push(qnode(u, 0));
- while(!pq.empty()){
- qnode x = pq.top(); pq.pop();
- int u = x.v;
- if(x.d != dist[u]) continue;
- for(unsigned i = 0; i < G[u].size(); ++i){
- int v = G[u][i].v;
- if(dist[u]+G[u][i].w < dist[v]){
- dist[v] = dist[u]+G[u][i].w;
- pq.push(qnode(v, dist[v]));
- }
- }
- }
- }
- /* 单源最短路径Bellman-Ford 算法
- * 时间复杂度O(VE);
- * 可处理负环;
- */
- int dist[maxn], n;
- struct Edge{
- int u, v, w;
- Edge(int _u, int _v, int _w): u(_u), v(_v), w(_w){}
- };
- vector<Edge> E;
- bool bellman_Ford(int s){
- for(int i = 0; i < n; ++i) dist[i] = INF;
- dist[s] = 0;
- for(int k = 0; k < n-1; ++k){ // 最多经过n-1个节点(迭代n-1次);
- bool flag = false;
- for(int i = 0; i < E.size(); ++i){ //检查每一条边;
- int u = E[i].u, v = E[i].v;
- if(dist[u] < INF && dist[v] > dist[u]+E[i].w){
- dist[v] = dist[u]+E[i].w;
- flag = true;
- }
- }
- if(!flag) return true; // 没有负环回路;
- }
- for(int i = 0; i < E.size(); ++i){
- int u = E[i].u, v = E[i].v;
- if(dist[v] > dist[u]+E[i].w) return false;
- }
- return true;
- }
- /* 单源最短路径SPFA算法
- * 时间复杂度O(KE) ; 复杂度不稳定;
- * 可处理负环;
- */
- int n, dist[maxn], cnt[maxn]; //判断一个节点在队列中出现的次数;
- bool inq[maxn]; // 判断是否在队列中;
- struct Edge{
- int v, w;
- Edge(int _v, int _w): v(_v), w(_w){};
- };
- vector<Edge> G[maxn];
- bool bellman_ford(int s){
- queue<int> q;
- memset(inq, 0, sizeof(inq));
- memset(cnt, 0, sizeof(cnt));
- for(int i = 0; i < n; ++i) dist[i] = INF;
- dist[s] = 0;
- inq[s] = true;
- q.push(s);
- while(!q.empty()){
- int u = q.front(); q.pop();
- inq[u] = false;
- for(int i = 0; i < G[u].size(); ++i){
- int v = G[u][i].v;
- if(dist[u] < INF && dist[v] > dist[u]+G[u][i].w){
- dist[v] = dist[u]+G[u][i].w;
- if(!inq[v]) { q.push(v); inq[v] = true; if(++cnt[v] > n) return false; }
- }
- }
- }
- return true;
- }
- /*
- *Floyd 算法;
- *调用之前简单的初始化 d[i][i] = 0, 其他d值为"INF";
- *复杂度O(N^3)
- */
- for(int k = 0; k < n; ++k)
- for(int i = 0; i < n; ++i)
- for(int j = 0; j < n; ++j)
- d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
- /* 最小生成树 prim 模板;
- * 时间复杂度 O(V*V); 适合稠密图;
- */
- int dist[maxn], G[maxn][maxn], n, m;
- bool vis[maxn];
- int prim(){
- int res = 0;
- memset(vis, 0, sizeof(vis));
- vis[0] = true;
- for(int i = 1; i < n; ++i) dist[i] = G[0][i];
- for(int k = 0; k < n-1; ++k){ // 还需要添加n-1个节点;
- int _min = INF, p = -1;
- for(int i = 0; i < n; ++i)
- if(!vis[i] && dist[i] < _min) _min = dist[i], p = i;
- if(_min == INF) return -1; //原图不连通;
- res += _min;
- vis[p] = true;
- for(int i = 0; i < n; ++i)
- if(!vis[i] && G[p][i] < dist[i]) dist[i] = G[p][i];
- }
- return res;
- }
- /*
- * 次小生成树;
- * 求最小生成树时, Max[i][j]表示MST中i到j最大边权;
- * 求完后,直接枚举所有不在MST中的边, 替换掉最大边的权, 跟新答案;
- */
- int n, m, G[maxn][maxn], dist[maxn], Max[maxn][maxn], pre[maxn];
- bool vis[maxn], used[maxn][maxn];
- int prim(){
- int res = 0;
- memset(vis, 0, sizeof(vis));
- memset(Max, 0, sizeof(Max));
- memset(used, 0, sizeof(used));
- vis[0] = 1;
- pre[0] = -1;
- for(int i = 1; i < n; ++i) { dist[i] = G[0][i]; pre[i] = 0; }
- for(int k = 0; k < n-1; ++k){
- int _min = INF, p = -1;
- for(int i = 0; i < n; ++i)
- if(!vis[i] && dist[i] < _min) _min = dist[i], p = i;
- if(_min == INF) return -1;
- res += _min;
- vis[p] = true;
- used[p][pre[p]] = used[pre[p]][p] = true;
- for(int i = 0; i < n; ++i){
- if(vis[i] && i != p) Max[i][p] = Max[p][i] = max(Max[i][pre[p]], dist[p]);
- if(!vis[i] && G[p][i] < dist[i]) { dist[i] = G[p][i]; pre[i] = p; }
- }
- }
- return res;
- }