一、图的存储
- 邻接矩阵 (顶点数<1000)
a[i][j] = 1
当输入数据是0 1矩阵时,适合采用邻接矩阵法
- 邻接表 (vector存储相邻顶点)
v[i] = {2,3,4}
- 结构体数组存边
edges[i] = Edge(x,y,z)
- 链式前向星
(head数组初始化-1 add函数添加边)
void add(int u, int v, int w){
edges[ct].val = w;
edges[ct].to = v;
edges[ct].next = head[u];
head[u] = ct++;
}
当输入输入格式为:
n m
x1 y1 z1
…
xm ym zm
或者输入格式为
n m
x1 y1
…
xm ym
上面四种都可以
二、图的遍历
- DFS 深度优先搜索 (栈)
- BFS 广度优先搜索 (队列)
三、最短路径
- Dijkstra算法(迪杰斯特拉算法) 单源最短路径 贪心
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int book[MAXN];
int dis[MAXN];
int n, m;
int Dijkstra(){
for(int i = 1; i <= n; i++){
dis[i] = a[1][i];
book[i] = 0;
}
book[1] = 1;
for(int i = 1; i < n; i++){
int u = 2, MIN = INT_MAX;
for(int j = 2; j <= n; j++){
if(book[j] == 0 && dis[j] < MIN){
u = j;
MIN = dis[j];
}
}
book[u] = 1;
for(int j = 2; j <= n; j++){
if(a[u][j] < INT_MAX && book[j] == 0 && dis[j] > dis[u] + a[u][j]){
dis[j] = dis[u]+a[u][j];
}
}
}
return dis[n];
}
int main(){
while(cin >> n >> m){
if(n == 0 && m == 0)break;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(i != j)
a[i][j] = INT_MAX;
else a[i][j] = 0;
}
}
for(int i = 1; i <= m; i++){
int x, y, z;
cin >> x >> y >> z;
a[x][y] = a[y][x] = z;
}
int sum = Dijkstra();
cout << sum << endl;
}
}
- Floyd算法(佛洛依德算法)多源最短路算法 动态规划
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int n, m;
void Floyd(){
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(a[i][j] > a[i][k] + a[k][j]){
a[i][j] = a[i][k] + a[k][j];
}
}
}
}
}
int main(){
while(cin >> n >> m){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(i != j)
a[i][j] = 9999999;
else a[i][j] = 0;
}
}
for(int i = 1; i <= m; i++){
int x, y, z;
cin >> x >> y >> z;
if(a[x][y] > z)
a[x][y] = a[y][x] = z;
}
Floyd();
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(j!=1){
cout << " ";
}
cout << a[i][j];
}
cout << endl;
}
}
}
四、最小生成树
- prim算法(普里姆算法) 贪心
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int dis[MAXN];//每个点到源点的距离
int book[MAXN];//每个点是否加入到最小生成树中
int n;
int Prim(){
for(int i = 1; i <= n; i++){
dis[i] = INT_MAX;
}
for(int i = 1; i <= n; i++){
book[i] = 0;
}
dis[1] = 0;
book[1] = 1;
for(int i = 1; i < n; i++){
int u=1, MIN = INT_MAX;//寻找离最小生成树距离最短的点
for(int j = 2; j <= n; j++){
if(book[j] == 0 && dis[j] < MIN){
MIN = dis[j];
u = j;
}
}
book[u] = 1;
for(int j = 1; j <= n; j++){
if(book[j] == 0 && dis[j] > a[u][j]){
dis[j] = a[u][j];
}
}
}
int sum = accumulate(dis+1, dis+n+1,0);
return sum;
}
int main(){
// ifstream cin("input.txt");
// ofstream cout("output.txt");
while(cin >> n ){
if(n == 0)break;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
a[i][j] = 0;
}
}
for(int i = 1; i <= n * ( n - 1) / 2; i++){
int x, y, z;
cin >> x >> y >> z;
a[x][y] = a[y][x] = z;
}
int sum = Prim();
cout << sum << endl;
}
}
- Kruskal算法 (克鲁斯卡尔算法)并查集
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int n;
struct Edge{
int x,y,z;
bool operator<(const Edge&that)const{
return z < that.z;
}
};
Edge edges[MAXN*MAXN];
int F[MAXN];
int getf(int x){
if(x == F[x]){
return F[x];
}else{
F[x] = getf(F[x]);
return F[x];
}
}
int Kruskal(){
int cnt = 0, sum = 0;
for(int i = 1; i <= n * ( n - 1) / 2; i++){
int t1 = getf(edges[i].x);
int t2 = getf(edges[i].y);
if(t1 != t2){
F[t2] = t1;
cnt++;
sum+=edges[i].z;
if(cnt>=n-1){
break;
}
}
}
if(cnt == n-1){
return sum;
}else{
return -1;
}
}
int main(){
// ifstream cin("input.txt");
// ofstream cout("output.txt");
while(cin >> n ){
if(n == 0)break;
for(int i = 1; i <= n; i++){
F[i] = i;//并查集初始化
}
for(int i = 1; i <= n * ( n - 1) / 2; i++){
int x, y, z,flag;
cin >> edges[i].x >> edges[i].y >> edges[i].z>>flag;//存边
if (flag == 1)//这一步很关键,表示已经修好的路
edges[i].z = 0;
}
sort(edges+1, edges+n * ( n - 1) / 2+1);
int sum = Kruskal();
cout << sum << endl;
}
}
五、注意事项
- 边的优化
当点的数量为n
时,最小生成树的边为n-1
,两两相邻的点至少需要有n*(n-1)/2
条边。
注意读入边的时候,是否有重复的边,根据需要过滤掉重复的边