赞
踩
- //有向图无环图中才有拓扑排序,且都是前面的编号的点指向后面编号的点
- #include<iostream>
- #include<cstring>
- using namespace std;
- const int N = 1e5 + 9;
- int e[N], ne[N], h[N], idx, n, m, d[N], q[N];
-
- void add(int a, int b)
- {
- e[idx] = b, ne[idx] = h[a], h[a] = idx++;
- }
-
- bool topsort()
- {
- int hh = 0, tt = -1;
- //将所有入度为0的点入队,编号从1~n
- for (int i = 1; i <= n; ++i) if (!d[i]) q[++tt] = i;
-
- while (hh <= tt)
- {
- int t = q[hh++];//出队
- for (int i = h[t]; i != -1; i = ne[i])
- {
- int j = e[i];
- d[j]--;//因为t指向j,又因为t删除了,所以j入度减一
- if (!d[j]) q[++tt] = j;
- }
- }
- return tt == (n - 1);//如果每个点都入队了表明为拓扑排序
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- memset(h, -1, sizeof h);
- cin >> n >> m;
- for (int i = 0; i < m; ++i)
- {
- int a, b; cin >> a >> b;
- add(a, b);
- d[b]++;//入度加一
- }
- //队列中的顺序刚好就是拓扑排序,排序不唯一
- if (topsort()) for (int i = 0; i < n; ++i) cout << q[i] << " ";
- else cout << -1;
- return 0;
- }

- //dijkstra堆优化
- #include<iostream>
- #include<cstring>
- #include<queue>
- using namespace std;
- const int N = 1e5 + 9;
- typedef pair<int, int> PII;
- int h[N], e[N], ne[N], idx, n, m, dist[N], w[N];
- bool st[N];
-
- void add(int x, int y, int z)
- {
- e[idx] = y, w[idx] = z, ne[idx] = h[x], h[x] = idx++;
- }
-
- int dijkstra()
- {
- memset(dist, 0x3f, sizeof dist);
- dist[1] = 0;
- //first是距离。second是编号
- //小根堆
- //有更改就加入
- //可能加入的点会有冗余,比如1号点一个是10,一个是15,所以用一个st[]数组来标记
- priority_queue<PII, vector<PII>, greater<PII>> heap;
- heap.push({ 0, 1 });
- while (heap.size())
- {
- //每次取出距离最小的点
- auto t = heap.top();
- heap.pop();
- int ver = t.second, distance = t.first;
- //不更新也对,不过会有很多冗余情况考虑
- //同一个顶点x,可能在队列中有(dis1, x)、(dis2, x)存在
- //而dis1 < dis2,所以(dis1, x)先出队并更新x的邻接顶点
- //当(dis2, x)出队的时候,由于dis2 > dis1
- //所以x的邻接顶点此时都不会更新,那么(dis2, x)这一步也就冗余了
- if (st[ver]) continue;//如果距离已经确定,则跳过该点
- st[ver] = true;
- for (int i = h[ver]; i != -1; i = ne[i])
- {
- int j = e[i];
- if (dist[j] > distance + w[i])
- {
- dist[j] = distance + w[i];
- heap.push({ dist[j], j });
- }
- }
- }
- if (dist[n] == 0x3f3f3f3f) return -1;
- return dist[n];
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- memset(h, -1, sizeof h);
- cin >> n >> m;
-
- while (m--)
- {
- int x, y, z; cin >> x >> y >> z;
- add(x, y, z);
- }
- cout << dijkstra();
- return 0;
- }

- //bellman_ford()算法
- //dijkstra()不能解决负权边最短路问题
- #include<iostream>
- #include<cstring>
- using namespace std;
- const int N = 510, M = 1e4 + 9;
- //back[]存储上一次的dist[]
- int dist[N], back[N], n, m, k;
-
- struct Edge
- {
- int a, b, w;
- } edges[M];
-
- int bellman_ford()
- {
- memset(dist, 0x3f, sizeof dist);
- dist[1] = 0;
- //存在边数限制,只能用bellman_ford做
- //关于为什么循环k次可以保证得到的dist就是相应k条件下的值?
- //当明白了备份的作用,就能理解了
- //备份的意义就是保证了每条边a->b在每次刷新时a都是独立的
- //在遍历过程中dist不会受其他点的影响,只和a上一次的值有关
- //而这个备份的操作就是放在k循环下的,所以得到的dist就是相应k条件下的值
- for (int i = 0; i < k; ++i)
- {
- //做备份
- //防止发生串联
- //串联:由于这个算法的特性决定,
- //每次更新得到的必然是在多考虑 1 条边之后能得到的全局的最短路
- //而串联指的是一次更新之后考虑了不止一条边:由于使用了松弛
- //某节点的当前最短路依赖于其所有入度的节点的最短路
- //假如在代码中使用dist[e.b]=min(dist[e.b],dist[e.a] + e.c)
- //我们无法保证dist[e.a]是否也在本次循环中被更新,如果被更新了
- //并且dist[e.b] > dist[e.a] + e.c
- //那么会造成当前节点在事实上“即考虑了一条从某个节点指向a的边
- //也考虑了a->b”,共两条边
- //而使用dist[e.b]=min(dist[e.b],last[e.a] + e.c)
- //可以保证a在dist更新后不影响对b的判定,因为后者使用last数组
- //保存着上一次循环中的dist的值
- memcpy(back, dist, sizeof dist);
- for (int j = 0; j < m; ++j)
- {
- int a = edges[j].a, b = edges[j].b, w = edges[j].w;
- dist[b] = min(dist[b], back[a] + w);
- }
- }
- //因为可能存在到n的路径中有负权边存在, 所以导致dist[n]的值小于0x3f3f3f3f
- //根据题目给的数据范围最多减去500*10000,也就是500w的值,所以大于一半就好
- //注意不要返回-1,因为-1可能就是dist[n]的距离
- if (dist[n] > 0x3f3f3f3f >> 1) return -0x3f3f3f3f;
- return dist[n];
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- cin >> n >> m >> k;
-
- for (int i = 0; i < m; ++i)
- {
- int x, y, z; cin >> x >> y >> z;
- edges[i].a = x, edges[i].b = y, edges[i].w = z;
- }
- int t = bellman_ford();
- if (t == -0x3f3f3f3f) cout << "impossible";
- else cout << dist[n];
- return 0;
- }

- //spfa
- #include<iostream>
- #include<cstring>
- #include<queue>
- using namespace std;
- const int N = 1e5 + 9;
- int h[N], e[N], ne[N], st[N], n, m, idx, w[N], dist[N];
-
- void add(int a, int b, int c)
- {
- e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
- }
-
- int spfa()
- {
- memset(dist, 0x3f, sizeof dist);
- dist[1] = 0;
- queue<int> q;//存储待更新点
- q.push(1);
- st[1] = true;
-
- while (q.size())
- {
- auto t = q.front();
- q.pop();
- st[t] = false;
-
- for (int i = h[t]; i != -1; i = ne[i])
- {
- int j = e[i];
- if (dist[j] > dist[t] + w[i])
- {
- dist[j] = dist[t] + w[i];
- if (!st[j])
- {
- q.push(j);
- st[j] = true;
- }
- }
- }
- }
- if (dist[n] == 0x3f3f3f3f) return -0x3f3f3f3f;
- return dist[n];
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- memset(h, -1, sizeof h);
- cin >> n >> m;
- while (m--)
- {
- int x, y, z; cin >> x >> y >> z;
- add(x, y, z);
- }
-
- int t = spfa();
- if (t == -0x3f3f3f3f) cout << "impossible";
- else cout << t;
- return 0;
- }

- //spfa判断负环
- #include<iostream>
- #include<queue>
- #include<cstring>
- using namespace std;
- const int N = 1e4 + 9;
- //cnt[]存储点最短路径的边数
- int e[N], ne[N], h[N], w[N], dist[N], cnt[N], idx, n, m;
-
- void add(int a, int b, int c)
- {
- e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
- }
-
- bool spfa()
- {
- memset(dist, 0x3f, sizeof dist);
- queue<int> q;
- //注意这里是讲所有点都入队,因为负环可能不在1号点这个路径上
- //这里dist初不初始化都行,但要是求单点到单点路径上是否存在回路,必须初始化dist
- //且dist[s] = 0
- for (int i = 1; i <= n; ++i) q.push(i);
-
- while (q.size())
- {
- auto t = q.front();
- q.pop();
-
- for (int i = h[t]; i != -1; i = ne[i])
- {
- int j = e[i];
- if (dist[j] > dist[t] + w[i])
- {
- dist[j] = dist[t] + w[i];
- cnt[j] = cnt[t] + 1;
- //边数 >= n,点数 >= n + 1,存在回路
- if (cnt[j] >= n) return true;
- q.push(j);
- }
- }
- }
- return false;
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- memset(h, -1, sizeof h);
- cin >> n >> m;
- while (m--)
- {
- int x, y, z; cin >> x >> y >> z;
- add(x, y, z);
- }
- if (spfa()) cout << "Yes";
- else cout << "No";
- return 0;
- }

- //Floyd求多源汇最短路
- #include<iostream>
- #include<cstring>
- using namespace std;
- const int N = 210, INF = 1e9;
- int n, m, Q, d[N][N];
-
- void floyd()
- {
- for (int k = 1; k <= n; ++k)
- {
- for (int i = 1; i <= n; ++i)
- {
- for (int j = 1; j <= n; ++j)
- {
- d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
- }
- }
- }
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- cin >> n >> m >> Q;
- for (int i = 1; i <= n; ++i)
- {
- for (int j = 1; j <= n; ++j)
- {
- if (i == j) d[i][j] = 0;
- else d[i][j] = INF;
- }
- }
-
- while (m--)
- {
- int a, b, z; cin >> a >> b >> z;
- d[a][b] = min(d[a][b], z);//选一个最短的重边
- }
-
- floyd();
-
- while (Q--)
- {
- int a, b; cin >> a >> b;
- //因为存在负权边,所以ab之间的距离可能小于无穷
- if (d[a][b] > INF / 2) cout << "impossible" << '\n';
- else cout << d[a][b] << '\n';
- }
-
- return 0;
- }

- //kruskal求最小生成树
- #include<iostream>
- #include<algorithm>
- using namespace std;
- const int N = 2e5 + 9;
-
- struct Edge
- {
- int a, b, w;
- bool operator< (const Edge& W) const
- {
- return w < W.w;
- }
- } edges[N];
-
- int n, m, p[N], res, cnt;
-
- int find(int x)
- {
- if (p[x] != x) p[x] = find(p[x]);
- return p[x];
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- cin >> n >> m;
-
- for (int i = 0; i < m; ++i)
- {
- int a, b, w; cin >> a >> b >> w;
- edges[i] = { a, b, w };
- }
- //从小到大排序
- sort(edges, edges + m);
- //并查集数组初始化
- for (int i = 1; i <= n; ++i) p[i] = i;
- //如果这个边与之前选择的所有边不会组成回路,就选择这条边分;反之,舍去。
- //判断是否会产生回路的方法为:使用并查集。
- //每次将未加入的边加入到集合中去
- for (int i = 0; i < m; ++i)
- {
- int a = edges[i].a, b = edges[i].b, w = edges[i].w;
- //不在一个集合里面
- a = find(a), b = find(b);
- if (a != b)
- {
- res += w;
- cnt++;
- p[a] = b;//加入集合
- }
- }
- //如果集合中的边数小于n - 1,说明不存在最小生成树
- if (cnt < n - 1) cout << "impossible";
- else cout << res;
- return 0;
- }

- #include<iostream>
- #include<cstring>
-
- using namespace std;
-
- const int N = 100010;
-
- int f[N];
- int e[N*2],h[N],ne[N*2],idx;
- //数组模拟邻接表模板
- void add(int a,int b)
- {
- e[idx]=b,ne[idx]=h[a],h[a]=idx++;
- }
- //并查集find函数模板:找到祖宗节点
- int find(int x)
- {
- if(f[x]!=x) f[x]=find(f[x]);
- return f[x];
- }
-
- int main()
- {
- int n,m;
- scanf("%d%d",&n,&m);
- memset(h,-1,sizeof h);
- //并查集初始化
- for(int i=1;i<=n;i++) f[i]=i;
- while(m--)
- {
- int a,b;
- scanf("%d%d",&a,&b);
- add(a,b);
- add(b,a);
- }
- //遍历每一个点
- for(int u=1;u<=n;u++)
- {
- //遍历当前点的邻接点
- for(int i=h[u];~i;i=ne[i])
- {
- int j=e[i];
- //如果邻接点和当前节点属于同一集合,不满足二分,直接NO
- if(find(j)==find(u))
- {
- puts("No");
- return 0;
- }
- //将所有邻接点合并到同一个集合
- f[find(e[h[u]])]=find(j);
- }
- }
- puts("Yes");
- return 0;
- }

- //二分图的最大匹配
- #include<iostream>
- #include<cstring>
- using namespace std;
- const int N = 510, M = 1e5 + 9;
- int h[N], e[M], ne[M], n1, n2, match[N], m, idx, res;
- bool st[N];
-
- void add(int a, int b)
- {
- e[idx] = b, ne[idx] = h[a], h[a] = idx++;
- }
-
- bool find(int x)
- {
- for (int i = h[x]; i != -1; i = ne[i])
- {
- int j = e[i];
- //没被遍历过
- if (!st[j])
- {
- //标记为匹配过
- st[j] = true;
- //如果这个姑娘还没有被匹配过,或者找到这个男生喜欢别的姑娘
- if (match[j] == 0 || find(match[j]))
- {
- match[j] = x;
- return true;
- }
- }
- }
- return false;
- }
-
- int main()
- {
- ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
- memset(h, -1, sizeof h);
-
- cin >> n1 >> n2 >> m;
-
- while (m--)
- {
- int a, b; cin >> a >> b;
- //不需要存双向边
- add(a, b);
- }
-
- for (int i = 1; i <= n1; ++i)
- {
- //每次将姑娘全初始化为没有遍历,因为之前别人喜欢的姑娘我也可能喜欢
- memset(st, false, sizeof st);
- if (find(i)) res++;//如果找到数量就加一
- }
- cout << res;
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。