图的遍历
//图
//dfs遍历(模板)
dfs(u){//访问顶点u(只能访问一个连通分量)
vis[u] = true;//设置u已被访问
for(从u出发能到达的所有顶点v)//枚举u出发可以到达的所有顶点v
if vis[v] == false;
dfs(v);//递归v
}
dfstrave(g){//遍历图g
for(g的所有顶点u)//对g的所有顶点u
if vis[u] == false;//如果未被访问
dfs[u]; //访问u所在的连通分量
}
//邻接矩阵版dfs
const int maxv = 1000;
const int inf = 1000000000;//
int n, g[maxv][maxv];
bool vis[maxv] = {false};
void dfs(int u, int depth){
vis[u] = true;//设置u已被访问
//如果需要对u进行操作,在此进行
//下面对所有从u出发能到达的分支顶点进行枚举
for(int v = 0; v < n; v++){
if(vis[v] == false&& g[u][v] != inf){//如果v未被访问,且u可到达v
dfs(v, depth + 1);//访问v,深度+1
}
}
}
void dfstrave(){
for(int u = 0; u < n; u++){
if(vis[u] == false){
dfs(u, 1);//访问u和u所在的连通块,1表示第一层
}
}
}
//邻接表版dfs
vector<int> adj[maxv];//图g的邻接表
int n;//顶点数n,maxv为最大顶点数
bool vis[maxv] = {false};//如果顶点i已被访问,则vis[i] == true,初值为false;
void dfs(int u, int depth){
vis[u] = true;
//如果需要对u进行操作,在此进行
for(int i = 0; i < adj[u].size(); i++){
int v = adj[u][i];
if(vis[v] == false){
dfs(v, depth + 1);
}
}
}
void dfstrave(){//遍历图g
for(int u = 0; u < n; u++){
if(vis[u] == false) dfs(u, 1);
}
}
//bfs(样板)
bfs(u){//遍历u的连通块
queue q;
inq[u] = true;
while(q!empty()){
取出q的队首元素,进行访问;
for(从u出发可达的所有顶点v){
if(inq[v] == false){
q.push(v);
inq[v] = true;
}
}
}
}
bfstrave(g){
for(g所有顶点u){//枚举g的所有顶点u
if(inq[u] == false){
bfs(u);
}
}
}
//邻接矩阵版bfs
int n, g[maxv][maxv];
bool inq[maxv] = {false};
void bfs(int u){//遍历u所在连通图
queue<int> q;
q.push(u);
inq[u] = true;
while(!=empty()){
int u = q.front();
q.pop();
for(int v = 0; v < n; v++){
if(inq[v] == false&&g[u][v] != inf){
q.push(v);
inq[v] = true;
}
}
}
}
void bfstrave(){
for(int u = 0; u < n; u++){
if(inq[u] == false){
bfs(u);
}
}
}
//邻接表版bfs
vector<int> adj[maxv];
int n;
bool inq[maxv] = {false};
void bfs(int u){
queue<int> q;
q.push(u);
inq[u] = true;
while(!=q.empty()){
int u = q.front();
q.pop;
for(int i = 0; i < adj[u].size(); i++){//对应u出发能到达的所有顶点
int v = adj[u][i];
if(inq[v] == false){
q.push(v);
inq[v] = true;
}
}
}
}
void bfstrave(){
for(int u = 0; u < n; u++){
if(inq[u] == false){
bfs(u);
}
}
}
//bfs邻接表考虑层数
struct node{
int v;//顶点编号
int layer;//顶点层号
};
vector<node> adj[n];
void bfs(int s){
queue<node> q;
node start;
start.v = s;
start.layer = 0;
q.push(satrt);
inq[start.v] = true;
while(!=empty()){
node topnode = q.front();
q.pop();
int u = topnode.v;
for(int i = 0; i < adj[u].size(); i++){
node next = adj[u][i];
next.layer = topnode.layer + 1;
if(inq[next.v] == false){
q.push(next);
inq[next.v] = true;
}
}
}
}
dijkstra
//最短路径
//dijkstra算法,(样板)
//g为图,一般设成全局变量;数组d为源点到达各点的最短路径长度,s为起点
dijkstra(g, d[], s){
初始化,
for(n次){//1,循环n次,每次从未选中的点中,选出离s最近的点u
u = 使d[u]最小的还未被访问的顶点的标号
记u已被访问;
for(从u出发能到达的所有顶点v){//2,从u能到达的点中选出从s->u->v的最短距离
if(v未被访问&&以u为中介点使s到顶点v的最短距离d[v]更优){
优化d[v];
}
}
}
}
//邻接矩阵版,O(V^2)
const int maxv = 1000;
const int inf = 1000000000;
int n, g[maxv][maxv];//g用来表示边权
int d[maxv];
bool vis[maxv] = {false};
void dijkstra(int s){
fill(d, d + maxv, inf){
d[s] = 0;
for(int i = 0; i < n; i++){//循环n次
int u = -1, min = inf;
for(int j = 0; j < n; j++){//1,
if(vis[j] == false&& d[j] < min){
u = j;
min = d[j];//选出最小d[u]
}
}
if(u == -1) return;
vis[u] = true;
for(int v = 0; v < n; v++){//2
if(vis[v] == false&&g[u][v] != inf && d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];//优化
}
}
}
}
}
//邻接表版
struct node{
int v, dis;//v表示点,dis表示边权
};
vector<node> adj[maxv];
int n;
int d[maxv];
bool vis[maxv] = {false};
void dijkstra(int s){
fill(d, d + maxv, inf);
d[s] = 0;
for(int i = 0; i < n; i++){//遍历n次
int u = -1, min = inf;
for(int j = 0; j < n; j++){//1
if(vis[j] == false&&d[j] < min){
u = j;
min = d[j];
}
}
if(u == -1) return;
vis[u] = true;
for(int j = 0; j < adj[u].size(); j++){//2
int v = adj[u][j].v;
if(vis[v] == false&&d[u] + adj[u][j].dis < d[v]){
d[v] = d[u] + adj[u][j].dis;//优化
}
}
}
}
dijkstra和简单第二标尺
//输出dijkstra的最短路径
//dijkstra + dfs
//邻接矩阵(只有一条路径时)
int n, g[maxv][maxv];
int d[maxv];
int pre[maxv];//新加
bool vis[maxv] = {false};
void dijkstra(int s){
fill(d, d + maxv, inf);
for(int i = 0; i < n; i++) pre[i] = i;//新加,初始化每个前驱为自己
d[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, min = inf;
for(int j = 0; j < n; j++){
if(vis[j] == false&&d[j] < min){
u = j;
min = d[j];
}
}
if(u = -1) return;
vis[u] = true;
for(int v = 0; v < n; v++){
if(vis[v] == false&&d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
pre[v] = u;//新加,v的前驱记为u
}
}
}
}
void dfs(int s, int v){//s为起点,v为当前访问的顶点编号
if(v == s){//边界
printf("%d\n", s);
return;
}
dfs(s, pre[v]);//递归
printf("%d\n", v);//从最深处回来,输出每层的顶点号
}
//新增边权
for(int v = 0; v < n; v++){//2.
if(vis[v] == false&&g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
c[v] = c[u] + cost[u][v];
}else if(d[u] + g[u][v] == d[v]&&c[u] + cost[u][v] < c[v]){
c[v] = c[u] + cost[u][v];
}
}
}
//新增点权
for(int v = 0; v < n; v++){
if(vis[v] == false&&g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
w[v] = w[u] + weight[v];
}else if(d[u] + g[u][v] == d[v]&&w[u] + weight[v] > w[v]){
w[v] = w[u] + weight[v];
}
}
}
//求最短路径条数
//需要新增一个num[],
for(int v = 0; v < n; v++){
if(vis[v] == false&& g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
num[v] = num[u];
}else if(d[u] + g[u][v] == d[v]){
num[v] += num[u];//累加num
}
}
}
dijkstra和dfs(较复杂的第二第三标尺)
//dijkstra + dfs
//用来解决较复杂的第二标尺
//1,先在dijkstra中记录下所有最短路径(只考虑路径),
//2,再从最短路径中选出第一条第二标尺最优路径
int num[maxv]; //用来记录最短路径条数(路径最短)
vector<int> pre[maxv];
void dijkstra(int s){
fill(d, d + maxv, inf);
d[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, min = inf;
for(int j = 0; j < n; j++){
if(vis[j] == false&&d[j] < min){
u = j;
min = d[j];
}
}
if(u == -1) return;
for(int v = 0; v < n; v++){
if(vis[v] == false&&g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
num[v] = num[u];
pre[v].clear();//清空pre[v]之前的路径
pre[v].push_back(u);//令v的前驱为u(因为u的前驱路径更优)
}else if(d[u] + g[u][v] == d[v]){
pre[v].push_back(u);//令v的前驱为u
num[v] += num[u];
}
}
}
}
}
int optvalue;
vector<int> pre[maxv];
vector<int> path, temppath;
void dfs(int v){//v为当前访问结点
if(v == st){
//递归边界,得到一条完整的temppath(优化)
temppath.push_back(v);
int value;
计算路径temppath上的value值;
if(value优于optvalue){
optvalue = value;
path = temppath;
}
temppath.pop_back();
return;
}
temppath.push_back(v);//递归前,先入矢量
for(int i = 0; i < pre[v].size(); i++){//遍历从v开始的所有短路径,(优化第二标尺)
dfs(pre[v][i]);
}
temppath.pop_back();//
}
//存放在temppath和path中的路径结点是逆序的,因此访问结点需要倒着进行
//计算temppath的边权之和
int value = 0;
for(int i = temppath.size() - 1; i > 0; i--){
//当前结点id,下一个结点innext
int id = temppath[i], idnext = temppath[i - 1];
value += v[id][idnext];//value增加边id->idnext的边权
}
//点权之和
int value = 0;
for(int i = temppath.size() - 1; i >= 0; i--){//倒着访问
int id = temppath[i];
value += w[id];//value增加结点id的点权
}
dijkstra实战
//对于a1030 travel plan
//dijkstra算法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxv = 510;
const int inf = 1000000000;
int n, m, st, ed, g[maxv][maxv], cost[maxv][maxv];
int d[maxv], c[maxv], pre[maxv];
bool vis[maxv] = {false};
void dijkstra(int s){
fill(d, d + maxv, inf);
fill(c, c + maxv, inf);
for(int i = 0; i < n; i++){
int u = -1, min = inf;
for(int j = 0; j < n; j++){
if(vis[j] == false&&d[j] < min){
u = j;
min = d[j];
}
}
if(u == -1) return;
vis[u] = true;
for(int v = 0; v < n; v++){
if(vis[v] == false&&g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = g[u][v] + d[u];
c[v] = cost[u][v] + c[u];
pre[v] = u;
}else if(d[u] + g[u][v] == d[v]&&c[u] + cost[u][v] < c[v]){
c[v] = cost[u][v] + c[u];
pre[v] = u;
}
}
}
}
}
void dfs(int v){//从v往回找st
if(v == st){
printf("%d", v);
return;
}
dfs(pre[v]);
printf("%d ", v);
}
int main(){
scanf("%d%d%d%d", &n, &m, &st, &ed);
int u, v;
fill(g[0], g[0] + maxv * maxv, inf);
for(int i = 0; i < m; i++){
scanf("%d%d", &u, &v);
scanf("%d%d", &g[u][v], &cost[u][v]);
g[v][u] = g[u][v];//无向图
cost[v][u] = cost[u][v];
}
dijkstra(st);
dfs(ed);//打印路径
printf("%d %d\n", d[ed], c[ed]);
return 0;
}
//dijkstra + dfs
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxv = 510;
const int inf = 1000000000;
int n, m, st, ed, g[maxv][maxv], cost[maxv][maxv];
int d[maxv], mincost = inf;
bool vis[maxv] = {false};
vector<int> pre[maxv];
vector<int> temppath, path;
void dijkstra(int s){//s为起点
fill(d, d + maxv, inf);
d[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, min = inf;
for(int j = 0; j < n; j++){
if(vis[j] == false&&d[j] < min){
u = j;
min = d[j];
}
}
if(u == -1) return;
vis[u] = true;
for(int v = 0; v < n; v++){
if(vis[v] == false&&g[u][v] != inf){
if(d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v];
pre[v].clear();
pre[v].push_back(u);
}else if(d[u] + g[u][v] == d[v]){
pre[v].push_back(u);
}
}
}
}
}
void dfs(int v){
if(v == st){
temppath.push_back(v);
int tempcost = 0;
for(int i = temppath.size() - 1;i > 0; i--){
int id = temppath[i], idnext = temppath[i-1];
tempcost += cost[id][idnext];
}
if(tempcost < mincost){//优化第二或第三标尺
mincost = temppath;
path = temppath;
}
temppath.pop_back();
return;
}
temppath.push_back(v);
for(int i = 0; i < pre[v].size(); i++){
dfs(pre[v][i]);
}
temppath.pop_back();
}
int main(){
scanf("%d%d%d%d", &n, &m, &st, &ed);
int u, v;
fill(g[0], g[0] + maxv * maxv, inf);
fill(cost[0], cost[0] + maxv * maxv, inf);
for(int i = 0; i < m; i++){
scanf("%d%d", &u, &v);
scanf("%d%d", &g[u][v], &cost[u][v]);
g[v][u] = g[u][v];
cost[v][u] = g[u][v];
}
dijkstra(st);
dfs(ed);
for(int i = path.size() - 1; i >= 0; i--){
printf("%d ", path[i]);
}
printf("%d %d\n", d[ed], mincost);
return 0;
}
处理负权点spfa
//SPFA算法,(解决有负权图的问题)
//模板SPFA(最短路径更快算法)
//期望时间复杂度为O(kE)
queue<int> q;
源点s入队
while(队列非空){
取出队首元素u;
for(u的所有邻接边u->v){
if(d[u] + dis < d[v]){
d[v] = d[u] + dis;
if(v当前不在队列){
v入队;
if(v入队次数大于n-1){
说明有可达负环,retur;
}
}
}
}
}
//bfs的SPFA //邻接表表示
struct node{
int v, dis;
};
vector<node> adj[maxv];
int n, d[maxv], num[maxv];
bool inq[maxv];
bool spfa(int s){
memset(inq, false, sizeof(inq));
memset(num, 0, sizeof(num));
fill(d, d + maxv, inf);//若是邻接矩阵则用d + maxv * maxv
//源点入队
queue<int> q;
q.push(s);
inq[s] = true;
num[s]++;
d[s] = 0;
//主体
while(!=q.empty()){
int u = q.front();
q.pop();
inq[u] = false;
//遍历u所有邻接边v
for(int j = 0; j < adj[u].size(); j++){
int v = adj[u][j].v;
int dis = adj[u][j].dis;
if(d[u] + dis < d[v]){//若可松弛
d[v] = d[u] + dis;
if(!inq[v]){//若v不在队列,(入队,边加一)
q.push(v);
inq[v] = true;
num[v]++;
if(num[v] >= n) return false;
}
}
}
}
return true;//无可达负环
}
//FIFO队列可替换成priority_queue, 加快速度
//或替换成双端队列deque, 加速
最小生成树kruskal(并查集的应用)
//最小生成树,
//kruskal算法(并查集的应用),采用边贪心策略
//kruskal算法模板
int kruskal(){
令最小生成树的边权之和为ans, 最小生成树的当前边数num_edge;
将所有边按边权从小到大排序;
for(从小到大枚举所有边){
if(当前测试的两个端点在不同的连通块中){
将该测试边加入最小生成树中;
ans += 测试边的边权;
最小生成树的当前边数num_edge加1;
当边数num_edge等于顶点数减1时结束循环;
}
}
return ans;
}
struct edge{
int u, v;//边的两端点
int cost;
}e[maxe];
bool cmp(edge a, edge b){//让边权从小到大排序
return a.cost < b.cost;
}
//kruskal算法O(eloge),用于边少
int father[n];
int findfather(int x){
if(x == father[x] return x;//边界
else{
t = findfather(father[x]);//递归式
father[x] = t;
return t;
}
}
int kruskal(int n, int m){
int ans = 0, num_edge = 0;//ans为边权之和,num_edge为当前生成树边数
for(int i = 0; i <= n; i++){
father[i] = i;//并查集初始化
}
sort(e, e + m, cmp);//对边从小到大排序
for(int i = 0; i < m; i++){//对于边i的两个顶点u,v
int fau = findfather(e[i].u);
int fav = findfather(e[i].v);
if(fau != fav){
father[fau] = fav;//把测试边加入最小生成树
ans += e[i].cost;
num_edge++;
if(num_edge == n-1) break;//边数为n-1就遍历完整个连通图
}
}
if(num_edge != n-1) return -1;//不是连通图,返回-1
else return ans;
}
kruskal实战
//kruskal实战
#include<iostream>
#include<algorithm>
using namespace std;
const int maxv = 110;
const int maxe = 10010;
//边集定义部分
struct edge{
int u, v;
int cost;
}e[maxe];
bool cmp(edge a, edge b){
return a.cost < b.cost;
}
//并查集部分
int father[maxv];
int findfather(int x){
if(x == father[x]) return x;
else{
int t = findfather(father[x]);
father[x] = t;
return t;
}
}
//kruskal部分
int kruskal(int n, int m){
int ans = 0, num_edge = 0;
for(int i = 0; i < n; i++){
father[i] = i;
}
sort(e, e + m, cmp);
for(int i = 0; i < m; i++){
int fau = findfather(e[i].u);
int fav = findfather(e[i].v);
if(fav != fau){
father[fav] = fau;
ans += e[i].cost;
num_edge++;
if(num_edge == n - 1) break;
}
}
if(num_edge != n - 1) return -1;
else return ans;
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].cost);
}
int ans = kruskal(n, m);
printf("%d\n", ans);
return 0;
}
forwards on weibo
//forwards on weibo
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxv = 1010;
struct node{
int id;
int layer;
};
vector<node> adj[maxv];
bool inq[maxv] = {false};
int bfs(int s, int l){
int numforward = 0;
queue<node> q;
node start;
start.id = s;
start.layer = 0;
q.push(start);
inq[start.id] = true;
while(!q.empty()){
node topnode = q.front();
q.pop();
int u = topnode.id;
for(int i = 0; i < adj[u].size(); i++){
node next = adj[u][i];
next.layer = topnode.layer + 1;
if(inq[next.id] == false&& next.layer <= l){
q.push(next);
inq[next.id] = true;
numforward++;
}
}
}
return numforward;
}
int main(){
node user;
int n, l, numfollow, idfollow;
scanf("%d%d", &n, &l);
for(int i = 1; i <= n; i++){
user.id = i;
scanf("%d", &numfollow);
for(int j = 0; j < numfollow; j++){
scanf("%d", &idfollow);
adj[idfollow].push_back(user);
}
}
int numquery, s;
scanf("%d", &numquery);
for(int i = 0; i < numquery; i++){
memset(inq, false, sizeof(inq));
scanf("%d", &s);
int numforward = bfs(s, l);
printf("%d\n", numforward);
}
return 0;
}
拓扑排序
//拓朴排序
//topo用来判断图是否为有向无环
//拓扑排序:在有向图中,对所有结点排序,要求没有一个结点指向它前面的结点
//对于priority_queue 可以保持队首是最小元素(set也可)
//邻接表版
vector<int> g[maxv];//g[v]表示v的出边
int n, m, in[maxv];//顶点数,入度
bool toposort(){
int num = 0;
queue<int> q;
for(int i = 0; i < n; i++){//将所有入度为0的顶点入队
if(in[i] == 0){
q.push(i);
}
}
while(!q.empty()){
int u = q.front();
//printf("%d", u); //可输出顶点u,作为拓扑排序的顶点
q.pop();
for(int i = 0; i < g[u].size(); i++){//对于u的所有出点v
int v = g[u][i];
in--;
if(in[v] == 0){
q.push(v);
}
}
g[u].clear();//清空u的出边,表示u的出点访问完(无必要可不写)
num++;
}
if(num == n) return true;//topo成功
else return false;
}
floyd(全源最短路径)
//floyd算法
//解决全源最短路径,(求任意两点u,v之间的最短路径长度,时间复杂度为O(n^3)),n在200以内
//模板
枚举顶点k∈[1,n]
以顶点k为中介点,枚举所有顶点对i和j(i∈[1, n], j∈[1, n])
如果dis[i][k] + dis[k][j] < dis[i][j]成立;
赋值dis[i][j] = dis[i][k] + dis[k][j];
//floyd算法代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf = 1000000000;
const int maxv = 200;
int n, m;
int dis[maxv][maxv];
void floyd(){
for(int k = 0; k < n; k++){//以k为中介点,则k在最前面枚举,
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(dis[i][k] != inf&&dis[k][j] != inf&&dis[i][k] + dis[k][j] < dis[i][j]){
dis[i][j] = dis[i][k] + dis[k][j];//找到更短路径
}
}
}
}
}
int main(){
int u, v, w;
fill(dis[0], dis[0] + maxv * maxv, inf);
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++){
dis[i][i] = 0;//顶点i到i距离为0
}
for(int i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = w;
}
floyd();//
for(int i = 0; i < n; i++){//输出最短路径,分别以0~n-1为起点到0~n-1的最短距离(全源最短路径)
for(int j = 0; j < n; j++){
printf("%d ", dis[i][j]);
}
printf("\n");
}
return 0;
}