#include<cstdio>#include<vector>#include<cstring>#definemaxn100usingnamespace std;int n, m;int u, v, cnt =0;
vector<int> edges[maxn];int visit[maxn];boolDFS(int idx){
visit[idx]=0;for(int i =0; i < edges[idx].size(); i++){int v = edges[idx][i];if(visit[v]==0){returntrue;}elseif(visit[v]==-1&&DFS(v)){returntrue;}}
visit[idx]=1;returnfalse;}intmain(){scanf("%d %d",&n,&m);bool flag =false;for(int i =0; i < m; i++){scanf("%d %d",&u,&v);
edges[u].push_back(v);}memset(visit,-1,sizeof(visit));for(int i =0; i < n; i++){if(visit[i]==-1){
flag =DFS(i);}if(flag){printf("Yes");return0;}}printf("No");return0;}
BFS
#include<cstdio>#include<vector>#include<queue>#definemaxn100usingnamespace std;intmain(){int n, m, s;int u, v;
queue<int> q;
vector<int> edges[maxn];int l[maxn];bool visit[maxn]={false};scanf("%d %d %d",&n,&m,&s);for(int i =0; i < m; i++){scanf("%d %d",&u,&v);
edges[u].push_back(v);
edges[v].push_back(u);}
q.push(s);
visit[s]=true;int idx, cnt, level =0;while(!q.empty()){
cnt = q.size();for(int i =0; i < cnt; i++){
idx = q.front();
l[idx]= level;
q.pop();for(int j =0; j < edges[idx].size(); j++){
v = edges[idx][j];if(!visit[v]){
q.push(v);
visit[v]=true;}}}
level ++;}for(int i =0; i < n; i++){printf("%d", l[i]);if(i != n-1)printf(" ");}return0;}
最短路径
Dijkstra
邻接矩阵版
Dijkstra只能处理非负权图
什么时候需要+DFS进行后处理?
#include<iostream>#defineMAX9999999// 表示边不存在,用INT32_MAX也可usingnamespace std;intmain(){int N, M;// 顶点数,边数int C1, C2;// 起点,终点int A[N][N];// 记录路径长度// 构造邻接矩阵
cin >> N >> M >> C1 >> C2;fill_n(&A[0][0], N * N, MAX);// MAX表示不可达for(int i =0; i < N; i++) cin >> V[i];int j, k;// 无向邻接图对称!for(int i =0; i < M; i++){
cin >> j >> k >> A[j][k];
A[k][j]= A[j][k];}// 最短路径算法int visit[N];// 记录是否在点集中int D[N];// 记录C1到点i的最短路径int min, v, w;// 初始化(直通路径初始化)for(int i =0; i < N; i++){
visit[i]=0;
D[i]= A[C1][i];// 记录直通的路径长}
visit[C1]=1;// C1加入已访问节点
D[C1]=0;// C1到C1路径长为0for(int i =1; i < N; i++){// N-1轮
min = MAX;
v =-1;for(w =0; w < N; w++){if(!visit[w]){if(D[w]< min){
v = w;
min = D[w];}}}if(v ==-1)break;
visit[v]=1;for(w =0; w < N; w++){//加入w节点,处理D[w]if(!visit[w]&& A[v][w]!= MAX){// 访问未访问且有直通路径的节点wif(min + A[v][w]< D[w]){// 如果当前记录的最短路径大于经过v的路径
D[w]= min + A[v][w];// 更新D[w]}}}}}
Bellman-Ford
可以处理负权边
SPFA
优化后的Bellman-Ford
Floyd
三重循环,k一定在最外层
#include<cstdio>#include<algorithm>#definemaxn100#defineinf999999usingnamespace std;intmain(){int n, m;int a[maxn][maxn];scanf("%d %d",&n,&m);int u, v, w;fill(&a[0][0],&a[0][0]+ maxn*maxn, inf);for(int i =0; i < m; i++){scanf("%d %d %d",&u,&v,&w);
a[u][v]= a[v][u]= w;}for(int i =0; i < n; i++){
a[i][i]=0;}for(int k =0; k < n; k++){for(int i =0; i < n; i++){for(int j =0; j < n; j++){if(a[i][k]!= inf && a[k][j]!= inf && a[i][j]> a[i][k]+ a[k][j]){
a[i][j]= a[i][k]+ a[k][j];}}}}for(int i =0; i < n; i++){for(int j =0; j < n; j++){if(a[i][j]== inf)printf("-1");elseprintf("%d", a[i][j]);if(j != n-1)printf(" ");}printf("\n");}}
最小生成树
Prim算法
和Dijkstra的思路很像,只有D[i]的含义不同
Dijkstra中,D[i]代表顶点i与起点的最短距离
Prim中,D[i]代表顶点i与集合的最短距离
#include<cstdio>#include<algorithm>#definemaxn100#defineinf999999usingnamespace std;intmain(){int n, m;int a[maxn][maxn];int D[maxn];bool visit[maxn];int u, v, weight;scanf("%d %d",&n,&m);fill(&a[0][0],&a[0][0]+ maxn*maxn, inf);fill(D, D + maxn, inf);fill(visit, visit + maxn,false);for(int i =0; i < m; i++){scanf("%d %d %d",&u,&v,&weight);
a[u][v]= a[v][u]= weight;}
D[0]=0;int mini;for(int i =0; i < n; i++){
mini = inf;
v =-1;for(int u =0; u < n; u++){if(!visit[u]&& D[u]< mini){
mini = D[u];
v = u;}}if(v ==-1){printf("-1");return0;}
visit[v]=true;for(int u =0; u < n; u++){if(!visit[u]&& a[v][u]!= inf){if(a[v][u]< D[u]){// 就素这里不一样
D[u]= a[v][u];}}}}int sum =0;for(int v =1; v < n; v++){
sum += D[v];}printf("%d", sum);}
Kruskal算法
并查集
emm,我选择prim
#include<cstdio>#include<algorithm>#definemaxn100usingnamespace std;structEdge{int v, u;int weight;} edges[maxn];int father[maxn];int n, m;voidinit(){for(int i =0; i < n; i++){
father[i]= i;}}intfindFather(int x){int a = x;while(x != father[x]){
x = father[x];}while(a != father[a]){int temp = a;
a = father[a];
father[temp]= x;}return x;}voidUnion(int a,int b){
a =findFather(a);
b =findFather(b);if(a != b){
father[a]= b;}}boolcmp(Edge e1, Edge e2){return e1.weight < e2.weight;}intmain(){int cnt =0, sum =0;scanf("%d %d",&n,&m);for(int i =0; i < m; i++){scanf("%d %d %d",&edges[i].v,&edges[i].u,&edges[i].weight);}init();sort(edges, edges + m, cmp);for(int i =0; i < m; i++){if(findFather(edges[i].v)!=findFather(edges[i].u)){Union(edges[i].u, edges[i].v);
sum += edges[i].weight;
cnt ++;if(cnt == n-1)break;}}if(cnt == n-1)printf("%d", sum);elseprintf("-1");}
拓扑排序
拓扑排序判环,输出拓扑排序也可,这里使用优先队列输出字典序最小的一条
找到当前入度0的结点,入队,删去与该结点相连的边(清空edges[i],对应邻点入度矩阵-1)
反复上一步,直到队列空
检查ans.size(),若<n则说明存在环,若==n则正确
#include<cstdio>#include<vector>#include<queue>#include<cstring>#definemaxn100usingnamespace std;intmain(){int n, m, v, u;
vector<int> edges[maxn];
vector<int> ans;
priority_queue<int, vector<int>, greater<int>> q;int indegree[maxn];scanf("%d %d",&n,&m);memset(indegree,0,sizeof(indegree));for(int i =0; i < m; i++){scanf("%d %d",&u,&v);
edges[u].push_back(v);
indegree[v]++;}for(int i =0; i < n; i++){if(indegree[i]==0){
q.push(i);}}int x;while(!q.empty()){
x = q.top();
ans.push_back(x);// 利用优先队列的性质,在pop的时候加入ans!
q.pop();for(int i =0; i < edges[x].size(); i++){
v = edges[x][i];
indegree[v]--;if(indegree[v]==0){
q.push(v);}}
edges[x].clear();}if(ans.size()== n)printf("Yes");elseprintf("No");}