最小生成树(求的就是给定一个无向图,挑选若干条边,连成一个树行图(无圈),使得所选边的权至和最小。)
Prim:
//prim算法
int prim(int n)
{
int i,j,min,pos;
int sum=0;
memset(isvisited,false,sizeof(isvisited));
//初始化
for (i=1;i<=n;i++)
{
dist[i]=map[1][i];
}
//从1开始
isvisited[1]=true;
dist[1]=MAX;
//找到权值最小点并记录下位置
for (i=1;i<n;i++)
{
min=MAX;
//pos=-1;
for (j=1;j<=n;j++)
{
if (!isvisited[j] && dist[j]<min)
{
min=dist[j];
pos=j;
}
}
sum+=dist[pos];//加上权值
isvisited[pos]=true;
//更新权值
for (j=1;j<=n;j++)
{
if (!isvisited[j] && dist[j]>map[pos][j])
{
dist[j]=map[pos][j];
}
}
}
return sum;
}
感悟: 最小生成树的代码与地杰斯特拉有着相像之处 ,prim算法是相加的是权重而地杰斯特拉增加的是下一个边
还有一种算法和prim的代码功能相同是kruskal,但是代码量大~
地杰斯特拉(求的是从一点到另一点的最短路径)(有向图)权值不能为负值
代码:
/***************************************
* About: 有向图的Dijkstra算法实现
***************************************/
#include <iostream>
using namespace std;
const int maxnum = 100;
const int maxint = 999999;
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
bool s[maxnum]; // 判断是否已存入该点到S集合中
for(int i=1; i<=n; ++i)
{
dist[i] = c[v][i];
s[i] = 0; // 初始都未用过该点
if(dist[i] == maxint)
prev[i] = 0;
else
prev[i] = v;
}
dist[v] = 0;
s[v] = 1;
// 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
// 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
for(int i=2; i<=n; ++i)
{
int tmp = maxint;
int u = v;
// 找出当前未使用的点j的dist[j]最小值
for(int j=1; j<=n; ++j)
if((!s[j]) && dist[j]<tmp)
{
u = j; // u保存当前邻接点中距离最小的点的号码
tmp = dist[j];
}
s[u] = 1; // 表示u点已存入S集合中
// 更新dist
for(int j=1; j<=n; ++j)
if((!s[j]) && c[u][j]<maxint)
{
int newdist = dist[u] + c[u][j]; //松弛就是这里与prim算法不同……
if(newdist < dist[j])
{
dist[j] = newdist;
prev[j] = u;
}
}
}
}
void searchPath(int *prev,int v, int u)
{
int que[maxnum];
int tot = 1;
que[tot] = u;
tot++;
int tmp = prev[u];
while(tmp != v)
{
que[tot] = tmp;
tot++;
tmp = prev[tmp];
}
que[tot] = v;
for(int i=tot; i>=1; --i)
if(i != 1)
cout << que[i] << " -> ";
else
cout << que[i] << endl;
}
int main()
{
freopen("input.txt", "r", stdin);
// 各数组都从下标1开始
int dist[maxnum]; // 表示当前点到源点的最短路径长度
int prev[maxnum]; // 记录当前点的前一个结点
int c[maxnum][maxnum]; // 记录图的两点间路径长度
int n, line; // 图的结点数和路径数
// 输入结点数
cin >> n;
// 输入路径数
cin >> line;
int p, q, len; // 输入p, q两点及其路径长度
// 初始化c[][]为maxint
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
c[i][j] = maxint;
for(int i=1; i<=line; ++i)
{
cin >> p >> q >> len;
if(len < c[p][q]) // 有重边
{
c[p][q] = len; // p指向q
c[q][p] = len; // q指向p,这样表示无向图
}
}
for(int i=1; i<=n; ++i)
dist[i] = maxint;
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
printf("%8d", c[i][j]);
printf("\n");
}
Dijkstra(n, 1, dist, prev, c);
// 最短路径长度
cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
// 路径
cout << "源点到最后一个顶点的路径为: ";
searchPath(prev, 1, n);
}
数据:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
floyd算法:(任意两点的最短路径)(有向图)
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int Ni=105;
const int INF=10000000;
int dist[Ni][Ni],g[Ni][Ni];
int fa[Ni][Ni],path[Ni];
int n,m,num,minc;
void Floyd()
{
int i,j,k,p,tmp;
minc=INF;
for(k=1;k<=n;k++)
{
for(i=1;i<k;i++)
for(j=i+1;j<k;j++)
{
tmp=dist[i][j]+g[i][k]+g[k][j];
if(tmp<minc) //找到更优解
{
minc=tmp;
num=0;
p=j;
while(p!=i) //逆向寻找前驱结点直到找到最前面的i,i->…->j
{
path[num++]=p;
p=fa[i][p];//fa[i][j]保存的不是k,而是fa[k][j].
}
path[num++]=i;
path[num++]=k;
}
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
tmp=dist[i][k]+dist[k][j];
if(dist[i][j]>tmp)
{
dist[i][j]=tmp;
fa[i][j]=fa[k][j];
}
}
}
}
int main()
{
int i,j,u,v,w;
while(~scanf("%d%d",&n,&m))
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
g[i][j]=INF;
dist[i][j]=INF;
fa[i][j]=i;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
w=min(g[u][v],w); //处理重边
g[u][v]=g[v][u]=dist[u][v]=dist[v][u]=w;
}
Floyd();
if(minc==INF)
printf("It's impossible.\n");
else
{
printf("%d\n",minc);
/*printf("%d",path[0]);
for(i=1;i<num;i++)
printf(" %d",path[i]);
printf("\n");*/
}
}
return 0;
}
from:http://acm.hdu.edu.cn/showproblem.php?pid=1599
bellman_fold算法:(含负权值有向图最优路径)
代码:
//x[i],y[i],w[i]分别是第 i条边的起点,终点和权值
const int MAXN=99999;
bool bellman_ford()
{
int i;
for (i=0;i<n;i++)
d[i]=MAXN; //初始化,设d[i]无穷大
d[0]=0; //第0个顶点处的距离为0
for (i=1;i<n;i++)
for (j=1;j<=m;j++)
if (d[x[j]]+w[j]<d[y[j]]) //枚举每一条边,并适当调整(取小权值)
{
d[y[j]]=d[x[j]]+w[j];
Prev[y[j]]=x[j];
}
for (i=1;i<=m;i++)
if (d[x[j]]+w[j]<d[y[j]])
return 0;
return 1;
}
spfa算法:(bellman_ford的优化,可求含负值的任意两点的距离 单源最短路径 有向图无向图
代码:
SPFA算法模板1:(邻接矩阵)
const int nMax = 1000; // 顶点的数量上限。
const int eMax = 2000; // 边的数量上限。
const int inf = 0xffffff;
int n, edge[nMax][nMax], dict[nMax];
int queue[eMax]; // 创建新队列,也可以用STL中的#include<queue>,但效率较低。
bool vis[nMax];
void init_data(s){
for(i = 1; i <= n; i ++) // 初始化dict[]。
dict[i] = inf;
dict[s] = 0; // 起点s。
}
void spfa(int s){ // 初始结点s,即为起点,若目标结点t,则输出dict[t]。
init_data(s);
int head = 0, tail = 1;
// int path[Max]; // 可增加一个path[]数组来记录最后s到t的路径。
queue[0] = x;
dict[s] = 0;
while(tail > head){
int u = queue[head];
vis[u] = true;
for(i = 1; i <= n; i ++){
if(dict[i] > dict[u] + edge[u][i]){
dict[i] = dict[u] + edge[u][i];
// path[i] = u;
if(!vis[i]){ // 对以前没有访问过的顶点,加入队列中。
vis[i] = true;
queue[tail] = i;
tail ++;
}
}
}
vis[u] = false; // 记得把出队列的顶点的vis[]置为false。
head ++;
}
}
SPFA算法模板2:(邻接表)
#include<iostream>
using namespace std;
#define MAXE 10000
#define MAXV 1000
int inf = 1000000000;
struct edge_t
{
int s, t;
int w;
int next;
};
edge_t arrEdge[MAXE]; //arrEdge[1...szEdge]保存szEdge条边.
int szEdge;
int arrHead[MAXV];
int nv;
int arrPre[MAXV];
int arrDis[MAXV];
int Que[MAXE*10]; //队列
char inQue[MAXV]; //点v是否在队列中
void SPFA(int s)
{
int i, ie, v, head, tail;
edge_t e;
for(i = 1; i <= nv; i++)
arrDis[i] = inf, arrPre[i] = i;
arrDis[s] = 0;
arrPre[s] = -1;
//初始化队列
memset(Que, 0, sizeof(Que));
memset(inQue, 0, sizeof(inQue));
head = 0, tail = 1;
Que[head] = s;
inQue[s] = 1;
while(head < tail)
{
v = Que[head++];
inQue[v] = 0;
for(ie = arrHead[v]; ie!=-1; ie = arrEdge[ie].next)
{
e = arrEdge[ie];
if(arrDis[e.t] > arrDis[v] + e.w)
{
arrDis[e.t] = arrDis[v] + e.w, arrPre[e.t] = v;
if(!inQue[e.t])
{
inQue[e.t] = 1;
Que[tail++] = e.t;
}
}
}
}
return ;
}
int main()
{
int ne, i, s, t, w, p;
memset(arrHead, -1, sizeof(arrHead));
memset(arrEdge, 0, sizeof(arrEdge));
szEdge = 0;
scanf("%d %d", &nv, &ne);
for(i = 1; i <= ne; i++)
{
scanf("%d %d %d", &s, &t, &w);
szEdge++;
arrEdge[szEdge].s = s;
arrEdge[szEdge].t = t;
arrEdge[szEdge].w = w;
arrEdge[szEdge].next = arrHead[s];
arrHead[s] = szEdge;
szEdge++;
arrEdge[szEdge].s = t;
arrEdge[szEdge].t = s;
arrEdge[szEdge].w = w;
arrEdge[szEdge].next = arrHead[t];
arrHead[t] = szEdge;
}
SPFA(1);
for(i = 1; i <= nv; i++)
{
printf("Distance from 1 to %d is %d.\nPath: %d", i, arrDis[i], i);
p = arrPre[i];
while(p != -1)
{
printf(" <-- %d", p);
p = arrPre[p];
}
printf("\n\n");
}
return 0;
}